From 2b81a6cfe4d3d03ce263094295aaec015d2f3687 Mon Sep 17 00:00:00 2001 From: Mark Pietrus Date: Sun, 5 Mar 2017 19:32:51 +0100 Subject: [PATCH] merge upstream (#2) * MAGETWO-64607: Implement custom provider to collect store and system configuration * MAGETWO-64608: Configure store and website reports for data collection * MAGETWO-64612: Implement config for Analytics cron setting * Create wishlist.js * Delete wishlist.js * Update requirejs-config.js * MAGETWO-64607: Implement custom provider to collect store and system configuration * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Defects fixed * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Defects fixed * MAGETWO-64629: Add plugin for base url change small refactoring * MAGETWO-64630: Create cronjob that executes update command * MAGETWO-64629: Add plugin for base url change review fixes * MTO-142: [Test] Captcha on front login - Defects fixed. * MTO-142: [Test] Captcha on front login - Defects fixed. * MAGETWO-64630: Create cronjob that executes update command * MAGETWO-63406: [Github] Paypal Payment Order Transaction ID Link will result to 404 not found - Fixed returned link - Fixed unit tests * Update Save.php If quantity is changed using mass action for attribute changes, but no stock item exists for the product, saving the newly created stockItem in StockItemRepository->save($stockItem) returns without saving the stockItem, since $stockItem->getProductId() is null, due to it being added to the array with key '0' (see line 165). To fix this, the array key has been changed to "product_id", such that StockItemRepository successfully creates the stockItem. Also, in my opinion, StockItemRepository->save() should not just return without notice, especially as it already encapsulates the functionality in a try/catch block. Throwing an exception would actually tell the user that something went wrong. * MAGETWO-64630: Create cronjob that executes update command * MAGETWO-64224: Remove usages of AttributeCache from customer module - fixing annotation * MAGETWO-64224: Remove usages of AttributeCache from customer module - making inheritdoc lowercase * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Defects fixed * MAGETWO-64612: Implement config for Analytics cron setting * MAGETWO-64629: Add plugin for base url change add secure url check * MAGETWO-64629: Add plugin for base url change add secure url check test * MAGETWO-64802: Implement corrupted data reporting for fieldDataConverter * MAGETWO-64505: [Integration] Advanced Analytics subscription create API request add data provider * MAGETWO-64505: [Integration] Advanced Analytics subscription create API request * MTO-111: [Test] Update Category if Use Category Flat (Cron is ON, "Update on Save" Mode) - Defects fixed. * MTO-113: [Test] Captcha on admin login - Defects fixed * MAGETWO-64625: Implement API for file downloading by MA allow only secure connections for analytics * MAGETWO-53457: [COMMUNITY] [FEEDBACK] Improve error reporting for failed order placement (checkout) #4682 updated unit tests * MTO-142: [Test] Captcha on front login - Defects fixed. * MTO-113: [Test] Captcha on admin login - Defects fixed * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Defects fixed * MAGETWO-64630: Create cronjob that executes update command * MAGETWO-65047 [GitHub][PR] Refactor contact module magento/magento2#8420 - fix code style issues * MAGETWO-65047 [GitHub][PR] Refactor contact module magento/magento2#8420 - fix integrity tests issues * MAGETWO-65047 [GitHub][PR] Refactor contact module magento/magento2#8420 - fix integration tests * MAGETWO-64902: [GitHub][PR] bug #8277 fixing bug with https downloading. magento/magento2#8278 * MAGETWO-64775: [GitHub] [PR] Make "is_required" and "is_visible" properties of telephone, company and fax attributes of addresses configurable magento/magento2#8519 - fix unit and integration tests * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Defects fixed * MAGETWO-64802: Implement corrupted data reporting for fieldDataConverter * MAGETWO-64625: Implement API for file downloading by MA add base64 encoding for transferring binary vector * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Defects fixed * MAGETWO-64613: Implement service to enrypt collected data * MAGETWO-64775: [GitHub] [PR] Make "is_required" and "is_visible" properties of telephone, company and fax attributes of addresses configurable magento/magento2#8519 - fix unit tests * MAGETWO-64613: Implement service to enrypt collected data * MAGETWO-64609: Extend modules report for data colection * MAGETWO-64802: Implement corrupted data reporting for fieldDataConverter * MAGETWO-64802: Implement corrupted data reporting for fieldDataConverter - covered skipping already converted data with integration test * MAGETWO-64608: Configure store and website reports for data collection * MAGETWO-64613: Implement service to enrypt collected data * MAGETWO-64505: [Integration] Advanced Analytics subscription create API request -- fix test * MAGETWO-62322: Performance Profile Generator * MAGETWO-62322: Performance Profile Generator * Add correct return type in order service The method return $this. I corrected the doc-block. * MAGETWO-64625: Implement API for file downloading by MA fix tests * MAGETWO-62549: Collect Data by cron and encrypt file -- fix code style * MAGETWO-64625: Implement API for file downloading by MA allow only secure connections for analytics * MAGETWO-62549: Collect Data by cron and encrypt file -- fix code style * MAGETWO-64802: Implement corrupted data reporting for fieldDataConverter * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Issue added to variation * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Defects fixed * MAGETWO-64626: Web API test for file export edit test for run on http * MAGETWO-65006: [GitHub][PR] Keep transparency when resizing images magento/magento2#7307 - fix is created based on public pull request magento/magento2#7307 by @kassner - closing magento/magento2#7307 * MAGETWO-51176: ST compiler supplies ObjectManagerInterface to direct third party dependencies * Fixed Doc Block for the dispatch method of the Rest Controller - provided more information on how dispatch method handles the Request * remove unused ComponentRegistrar instance from being passed to Populator::populateMappings * Fixed return type of OrderRepository::getList * MAGETWO-64802: Implement corrupted data reporting for fieldDataConverter - custom converters coverage * MAGETWO-64626: Web API test for file export edit test for run on http * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Defects fixed * MAGETWO-64626: Web API test for file export edit test for run on http * MAGETWO-64626: Web API test for file export allow only secure connections for analytics * MTO-111: [Test] Update Category if Use Category Flat (Cron is ON, "Update on Save" Mode) - Duplicated new line removed. * MAGETWO-64626: Web API test for file export edit test for run on http * MTO-146: [Test] Create offline Order from "Edit Customer" Admin page - Defects fixed * MAGETWO-64802: Implement corrupted data reporting for fieldDataConverter - custom converters - fixed integration test and removed dependency * MTO-142: [Test] Captcha on front login - Defects fixed. * MAGETWO-64796: Added fixture for Customer module with assigned website * MAGETWO-64601: [Integration] report page API request for Advanced analytics * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-61095: When attempting to place a reorder after a product is disabled, product still gets added to the cart * MAGETWO-64515: change comment in file due to PR risk * MAGETWO-64515: change comment in file due to PR risk * MAGETWO-62168: Improve field data converter logging and error reporting * MAGETWO-62168: Improve field data converter logging and error reporting * MAGETWO-62168: Improve field data converter logging and error reporting * MAGETWO-62168: Improve field data converter logging and error reporting * MAGETWO-62168: Improve field data converter logging and error reporting * MAGETWO-62168: Improve field data converter logging and error reporting --- .gitignore | 5 + .php_cs | 2 +- .travis.yml | 4 +- Gruntfile.js.sample | 2 +- README.md | 59 +- app/autoload.php | 2 +- app/bootstrap.php | 23 +- .../Block/Grid/Renderer/Actions.php | 2 +- .../Block/Grid/Renderer/Notice.php | 2 +- .../Block/Grid/Renderer/Severity.php | 2 +- .../Magento/AdminNotification/Block/Inbox.php | 2 +- .../Block/System/Messages.php | 2 +- .../System/Messages/UnreadMessagePopup.php | 2 +- .../AdminNotification/Block/ToolbarEntry.php | 2 +- .../AdminNotification/Block/Window.php | 2 +- .../Controller/Adminhtml/Notification.php | 2 +- .../Adminhtml/Notification/AjaxMarkAsRead.php | 2 +- .../Adminhtml/Notification/Index.php | 2 +- .../Adminhtml/Notification/MarkAsRead.php | 2 +- .../Adminhtml/Notification/MassMarkAsRead.php | 2 +- .../Adminhtml/Notification/MassRemove.php | 2 +- .../Adminhtml/Notification/Remove.php | 2 +- .../Adminhtml/System/Message/ListAction.php | 2 +- .../Model/Config/Source/Frequency.php | 2 +- .../Magento/AdminNotification/Model/Feed.php | 2 +- .../Magento/AdminNotification/Model/Inbox.php | 2 +- .../Model/InboxInterface.php | 2 +- .../Model/NotificationService.php | 2 +- .../Model/ResourceModel/Grid/Collection.php | 2 +- .../Model/ResourceModel/Inbox.php | 2 +- .../Model/ResourceModel/Inbox/Collection.php | 2 +- .../Inbox/Collection/Critical.php | 2 +- .../ResourceModel/Inbox/Collection/Unread.php | 2 +- .../Model/ResourceModel/System/Message.php | 2 +- .../System/Message/Collection.php | 2 +- .../Message/Collection/Synchronized.php | 2 +- .../Model/System/Message.php | 2 +- .../Model/System/Message/Baseurl.php | 2 +- .../Model/System/Message/CacheOutdated.php | 2 +- .../Message/Media/AbstractSynchronization.php | 2 +- .../Message/Media/Synchronization/Error.php | 2 +- .../Message/Media/Synchronization/Success.php | 2 +- .../Model/System/Message/Security.php | 2 +- ...edispatchAdminActionControllerObserver.php | 2 +- .../AdminNotification/Setup/InstallSchema.php | 2 +- .../Test/Unit/Block/ToolbarEntryTest.php | 2 +- .../Test/Unit/Model/FeedTest.php | 2 +- .../Unit/Model/NotificationServiceTest.php | 2 +- .../System/Message/CacheOutdatedTest.php | 2 +- .../Media/Synchronization/ErrorTest.php | 2 +- .../Model/System/Message/SecurityTest.php | 2 +- .../Component/DataProvider/DataProvider.php | 2 +- .../Magento/AdminNotification/composer.json | 2 +- .../Magento/AdminNotification/etc/acl.xml | 2 +- .../AdminNotification/etc/adminhtml/di.xml | 2 +- .../etc/adminhtml/events.xml | 2 +- .../AdminNotification/etc/adminhtml/menu.xml | 2 +- .../etc/adminhtml/routes.xml | 2 +- .../etc/adminhtml/system.xml | 2 +- .../Magento/AdminNotification/etc/config.xml | 2 +- app/code/Magento/AdminNotification/etc/di.xml | 2 +- .../Magento/AdminNotification/etc/module.xml | 2 +- .../AdminNotification/registration.php | 2 +- .../layout/adminhtml_notification_block.xml | 2 +- .../layout/adminhtml_notification_index.xml | 2 +- .../view/adminhtml/layout/default.xml | 2 +- .../view/adminhtml/requirejs-config.js | 4 +- .../templates/notification/window.phtml | 4 +- .../adminhtml/templates/system/messages.phtml | 2 +- .../templates/system/messages/popup.phtml | 2 +- .../adminhtml/templates/toolbar_entry.phtml | 2 +- .../ui_component/notification_area.xml | 2 +- .../adminhtml/web/js/grid/columns/message.js | 2 +- .../view/adminhtml/web/js/grid/listing.js | 2 +- .../view/adminhtml/web/system/notification.js | 13 +- .../web/template/grid/cells/message.html | 4 +- .../adminhtml/web/template/grid/listing.html | 2 +- .../view/adminhtml/web/toolbar_entry.js | 67 +- .../Controller/Adminhtml/Export/GetFilter.php | 2 +- .../Model/Export/AdvancedPricing.php | 31 +- .../Model/Import/AdvancedPricing.php | 39 +- .../Import/AdvancedPricing/Validator.php | 2 +- .../AdvancedPricing/Validator/TierPrice.php | 6 +- .../Validator/TierPriceType.php | 48 + .../AdvancedPricing/Validator/Website.php | 2 +- .../Indexer/Product/Price/Plugin/Import.php | 2 +- .../Unit/Model/Export/AdvancedPricingTest.php | 2 +- .../Validator/TierPriceTest.php | 2 +- .../Validator/TierPriceTypeTest.php | 78 + .../AdvancedPricing/Validator/WebsiteTest.php | 2 +- .../Import/AdvancedPricing/ValidatorTest.php | 2 +- .../Unit/Model/Import/AdvancedPricingTest.php | 152 +- .../Product/Price/Plugin/ImportTest.php | 2 +- .../AdvancedPricingImportExport/composer.json | 2 +- .../etc/adminhtml/routes.xml | 4 +- .../AdvancedPricingImportExport/etc/di.xml | 3 +- .../etc/export.xml | 2 +- .../etc/import.xml | 2 +- .../etc/module.xml | 2 +- .../registration.php | 2 +- .../Analytics/Api/Data/LinkInterface.php | 36 + .../Analytics/Api/LinkProviderInterface.php | 17 + .../System/Config/SubscriptionStatusLabel.php | 66 + .../Controller/Adminhtml/BasicTier/SignUp.php | 62 + .../Controller/Adminhtml/Reports/Show.php | 71 + .../Adminhtml/Subscription/Activate.php | 118 + .../Adminhtml/Subscription/Postpone.php | 101 + .../Magento/Analytics/Cron/CollectData.php | 53 + app/code/Magento/Analytics/Cron/SignUp.php | 131 + app/code/Magento/Analytics/Cron/Update.php | 78 + app/code/Magento/Analytics/LICENSE.txt | 48 + app/code/Magento/Analytics/LICENSE_AFL.txt | 48 + .../Analytics/Model/AnalyticsToken.php | 92 + .../Model/Condition/CanViewNotification.php | 68 + app/code/Magento/Analytics/Model/Config.php | 40 + .../Model/Config/Backend/CollectionTime.php | 91 + .../Model/Config/Backend/Enabled.php | 71 + .../Backend/Enabled/SubscriptionHandler.php | 174 + .../Model/Config/Backend/Vertical.php | 32 + .../Magento/Analytics/Model/Config/Data.php | 29 + .../Magento/Analytics/Model/Config/Mapper.php | 66 + .../Magento/Analytics/Model/Config/Reader.php | 52 + .../Analytics/Model/Config/Reader/Xml.php | 61 + .../Analytics/Model/Config/SchemaLocator.php | 56 + .../Model/Config/Source/Vertical.php | 51 + .../Analytics/Model/ConfigInterface.php | 22 + .../Magento/Analytics/Model/Connector.php | 64 + .../Model/Connector/CommandInterface.php | 20 + .../Model/Connector/Http/Client/Curl.php | 76 + .../Model/Connector/Http/ClientInterface.php | 29 + .../Model/Connector/Http/ResponseFactory.php | 23 + .../Analytics/Model/Connector/OTPRequest.php | 148 + .../Model/Connector/SignUpCommand.php | 74 + .../Model/Connector/SignUpRequest.php | 135 + .../Model/Connector/UpdateCommand.php | 124 + .../Magento/Analytics/Model/Cryptographer.php | 130 + .../Analytics/Model/EncodedContext.php | 52 + .../Analytics/Model/ExportDataHandler.php | 205 + app/code/Magento/Analytics/Model/FileInfo.php | 52 + .../Analytics/Model/FileInfoManager.php | 122 + .../Magento/Analytics/Model/FileRecorder.php | 137 + .../Magento/Analytics/Model/FlagManager.php | 93 + .../Analytics/Model/IntegrationManager.php | 126 + app/code/Magento/Analytics/Model/Link.php | 60 + .../Magento/Analytics/Model/LinkProvider.php | 67 + .../Analytics/Model/NotificationTime.php | 65 + .../Model/Plugin/BaseUrlConfigPlugin.php | 101 + .../Analytics/Model/ProviderFactory.php | 39 + .../Analytics/Model/ReportUrlProvider.php | 75 + .../Magento/Analytics/Model/ReportWriter.php | 102 + .../Analytics/Model/ReportWriterInterface.php | 28 + .../Model/ReportXml/ModuleIterator.php | 48 + .../Model/StoreConfigurationProvider.php | 102 + .../Magento/Analytics/Model/Subscription.php | 108 + .../Model/SubscriptionStatusProvider.php | 109 + app/code/Magento/Analytics/README.md | 12 + .../Magento/Analytics/ReportXml/Config.php | 43 + .../ReportXml/Config/Converter/Xml.php | 61 + .../Analytics/ReportXml/Config/Data.php | 29 + .../Analytics/ReportXml/Config/Mapper.php | 38 + .../Analytics/ReportXml/Config/Reader.php | 57 + .../Analytics/ReportXml/Config/Reader/Xml.php | 56 + .../ReportXml/Config/SchemaLocator.php | 51 + .../Analytics/ReportXml/ConfigInterface.php | 23 + .../Analytics/ReportXml/ConnectionFactory.php | 65 + .../DB/Assembler/AssemblerInterface.php | 27 + .../DB/Assembler/FilterAssembler.php | 64 + .../ReportXml/DB/Assembler/FromAssembler.php | 63 + .../ReportXml/DB/Assembler/JoinAssembler.php | 105 + .../ReportXml/DB/ColumnsResolver.php | 69 + .../ReportXml/DB/ConditionResolver.php | 163 + .../Analytics/ReportXml/DB/NameResolver.php | 40 + .../ReportXml/DB/ReportValidator.php | 64 + .../Analytics/ReportXml/DB/SelectBuilder.php | 289 + .../ReportXml/DB/SelectBuilderFactory.php | 43 + .../Analytics/ReportXml/IteratorFactory.php | 61 + .../Magento/Analytics/ReportXml/Query.php | 96 + .../Analytics/ReportXml/QueryFactory.php | 142 + .../Analytics/ReportXml/ReportProvider.php | 79 + .../Analytics/ReportXml/SelectHydrator.php | 96 + .../Magento/Analytics/Setup/InstallData.php | 43 + .../Adminhtml/BasicTier/SignUpTest.php | 84 + .../Controller/Adminhtml/Reports/ShowTest.php | 155 + .../Adminhtml/Subscription/ActivateTest.php | 229 + .../Adminhtml/Subscription/PostponeTest.php | 168 + .../Test/Unit/Cron/CollectDataTest.php | 92 + .../Analytics/Test/Unit/Cron/SignUpTest.php | 171 + .../Analytics/Test/Unit/Cron/UpdateTest.php | 93 + .../Test/Unit/Model/AnalyticsTokenTest.php | 129 + .../Condition/CanViewNotificationTest.php | 81 + .../Config/Backend/CollectionTimeTest.php | 112 + .../Enabled/SubscriptionHandlerTest.php | 187 + .../Unit/Model/Config/Backend/EnabledTest.php | 99 + .../Model/Config/Backend/VerticalTest.php | 59 + .../Test/Unit/Model/Config/MapperTest.php | 142 + .../Test/Unit/Model/Config/ReaderTest.php | 108 + .../Unit/Model/Config/SchemaLocatorTest.php | 80 + .../Unit/Model/Config/Source/VerticalTest.php | 60 + .../Analytics/Test/Unit/Model/ConfigTest.php | 68 + .../Model/Connector/Http/Client/CurlTest.php | 203 + .../Unit/Model/Connector/OTPRequestTest.php | 275 + .../Model/Connector/SignUpCommandTest.php | 126 + .../Model/Connector/SignUpRequestTest.php | 226 + .../Model/Connector/UpdateCommandTest.php | 139 + .../Test/Unit/Model/ConnectorTest.php | 70 + .../Test/Unit/Model/CryptographerTest.php | 226 + .../Test/Unit/Model/EncodedContextTest.php | 61 + .../Test/Unit/Model/ExportDataHandlerTest.php | 270 + .../Test/Unit/Model/FileInfoManagerTest.php | 194 + .../Test/Unit/Model/FileInfoTest.php | 62 + .../Test/Unit/Model/FileRecorderTest.php | 209 + .../Test/Unit/Model/FlagManagerTest.php | 123 + .../Unit/Model/IntegrationManagerTest.php | 228 + .../Test/Unit/Model/LinkProviderTest.php | 162 + .../Test/Unit/Model/NotificationTimeTest.php | 76 + .../Model/Plugin/BaseUrlConfigPluginTest.php | 190 + .../Test/Unit/Model/ReportUrlProviderTest.php | 122 + .../Test/Unit/Model/ReportWriterTest.php | 213 + .../Model/ReportXml/ModuleIteratorTest.php | 50 + .../Model/StoreConfigurationProviderTest.php | 123 + .../Model/SubscriptionStatusProviderTest.php | 94 + .../Test/Unit/Model/SubscriptionTest.php | 197 + .../ReportXml/Config/Converter/XmlTest.php | 121 + .../Test/Unit/ReportXml/Config/MapperTest.php | 47 + .../ReportXml/Config/SchemaLocatorTest.php | 74 + .../ReportXml/Config/_files/valid_reports.xml | 25 + .../Test/Unit/ReportXml/ConfigTest.php | 64 + .../Unit/ReportXml/ConnectionFactoryTest.php | 106 + .../DB/Assembler/FilterAssemblerTest.php | 143 + .../DB/Assembler/FromAssemblerTest.php | 121 + .../DB/Assembler/JoinAssemblerTest.php | 261 + .../Unit/ReportXml/DB/ColumnsResolverTest.php | 108 + .../ReportXml/DB/ConditionResolverTest.php | 102 + .../Unit/ReportXml/DB/NameResolverTest.php | 90 + .../Unit/ReportXml/DB/ReportValidatorTest.php | 125 + .../Unit/ReportXml/DB/SelectBuilderTest.php | 103 + .../Unit/ReportXml/IteratorFactoryTest.php | 59 + .../Test/Unit/ReportXml/QueryFactoryTest.php | 239 + .../Test/Unit/ReportXml/QueryTest.php | 90 + .../Unit/ReportXml/ReportProviderTest.php | 180 + .../Unit/ReportXml/SelectHydratorTest.php | 113 + .../Ui/DataProvider/DummyDataProviderTest.php | 228 + .../Ui/DataProvider/DummyDataProvider.php | 237 + app/code/Magento/Analytics/composer.json | 27 + app/code/Magento/Analytics/etc/acl.xml | 30 + .../Magento/Analytics/etc/adminhtml/menu.xml | 13 + .../Analytics/etc/adminhtml/routes.xml | 14 + .../Analytics/etc/adminhtml/system.xml | 43 + app/code/Magento/Analytics/etc/analytics.xml | 50 + app/code/Magento/Analytics/etc/analytics.xsd | 80 + app/code/Magento/Analytics/etc/config.xml | 26 + app/code/Magento/Analytics/etc/crontab.xml | 14 + app/code/Magento/Analytics/etc/di.xml | 147 + app/code/Magento/Analytics/etc/module.xml | 18 + app/code/Magento/Analytics/etc/reports.xml | 48 + app/code/Magento/Analytics/etc/reports.xsd | 87 + app/code/Magento/Analytics/etc/webapi.xml | 16 + app/code/Magento/Analytics/registration.php | 11 + .../layout/adminhtml_dashboard_index.xml | 21 + .../adminhtml/templates/dashboard/link.phtml | 13 + .../analytics_subscription_form.xml | 114 + .../adminhtml/web/js/modal/modal-component.js | 66 + .../Authorization/Model/Acl/AclRetriever.php | 2 +- .../Authorization/Model/Acl/Loader/Role.php | 69 +- .../Authorization/Model/Acl/Loader/Rule.php | 77 +- .../Authorization/Model/Acl/Role/Generic.php | 2 +- .../Authorization/Model/Acl/Role/Group.php | 2 +- .../Authorization/Model/Acl/Role/User.php | 2 +- .../Model/CompositeUserContext.php | 2 +- .../ResourceModel/Permissions/Collection.php | 2 +- .../Model/ResourceModel/Role.php | 2 +- .../Model/ResourceModel/Role/Collection.php | 2 +- .../ResourceModel/Role/Grid/Collection.php | 2 +- .../Model/ResourceModel/Rules.php | 21 +- .../Model/ResourceModel/Rules/Collection.php | 2 +- app/code/Magento/Authorization/Model/Role.php | 2 +- .../Magento/Authorization/Model/Rules.php | 2 +- .../Model/UserContextInterface.php | 2 +- .../Setup/AuthorizationFactory.php | 2 +- .../Authorization/Setup/InstallData.php | 2 +- .../Authorization/Setup/InstallSchema.php | 2 +- .../Test/Unit/Model/Acl/AclRetrieverTest.php | 2 +- .../Test/Unit/Model/Acl/Loader/RoleTest.php | 170 +- .../Test/Unit/Model/Acl/Loader/RuleTest.php | 94 +- .../Unit/Model/CompositeUserContextTest.php | 2 +- .../Unit/Model/ResourceModel/RulesTest.php | 223 + app/code/Magento/Authorization/composer.json | 2 +- app/code/Magento/Authorization/etc/di.xml | 2 +- app/code/Magento/Authorization/etc/module.xml | 2 +- .../Magento/Authorization/registration.php | 2 +- .../Order/View/Info/FraudDetails.php | 2 +- .../Authorizenet/Block/Transparent/Iframe.php | 2 +- .../Directpost/Payment/AddConfigured.php | 2 +- .../Directpost/Payment/Cancel.php | 2 +- .../Payment/ConfigureProductToAdd.php | 2 +- .../Payment/ConfigureQuoteItems.php | 2 +- .../Authorizenet/Directpost/Payment/Index.php | 2 +- .../Directpost/Payment/LoadBlock.php | 2 +- .../Authorizenet/Directpost/Payment/Place.php | 2 +- .../Directpost/Payment/ProcessData.php | 2 +- .../Directpost/Payment/Redirect.php | 2 +- .../Directpost/Payment/Reorder.php | 2 +- .../Directpost/Payment/ReturnQuote.php | 2 +- .../Authorizenet/Directpost/Payment/Save.php | 2 +- .../Directpost/Payment/ShowUpdateResult.php | 2 +- .../Authorizenet/Directpost/Payment/Start.php | 2 +- .../Controller/Directpost/Payment.php | 8 +- .../Directpost/Payment/BackendResponse.php | 5 +- .../Controller/Directpost/Payment/Place.php | 18 +- .../Directpost/Payment/Redirect.php | 2 +- .../Directpost/Payment/Response.php | 5 +- .../Directpost/Payment/ReturnQuote.php | 2 +- .../Authorizenet/Helper/Backend/Data.php | 2 +- app/code/Magento/Authorizenet/Helper/Data.php | 2 +- .../Authorizenet/Helper/DataFactory.php | 2 +- .../Authorizenet/Model/Authorizenet.php | 6 +- app/code/Magento/Authorizenet/Model/Debug.php | 2 +- .../Magento/Authorizenet/Model/Directpost.php | 2 +- .../Authorizenet/Model/Directpost/Request.php | 6 +- .../Model/Directpost/Request/Factory.php | 2 +- .../Model/Directpost/Response.php | 2 +- .../Model/Directpost/Response/Factory.php | 2 +- .../Authorizenet/Model/Directpost/Session.php | 2 +- .../Magento/Authorizenet/Model/Request.php | 2 +- .../Authorizenet/Model/Request/Factory.php | 2 +- .../Model/ResourceModel/Debug.php | 2 +- .../Model/ResourceModel/Debug/Collection.php | 2 +- .../Magento/Authorizenet/Model/Response.php | 2 +- .../Authorizenet/Model/Response/Factory.php | 2 +- .../Authorizenet/Model/Source/Cctype.php | 4 +- .../Model/Source/PaymentAction.php | 2 +- .../Authorizenet/Model/TransactionService.php | 2 +- .../Observer/AddFieldsToResponseObserver.php | 2 +- .../Observer/SaveOrderAfterSubmitObserver.php | 2 +- .../UpdateAllEditIncrementsObserver.php | 2 +- .../Directpost/Payment/RedirectTest.php | 2 +- .../Directpost/Payment/PlaceTest.php | 50 +- .../Directpost/Payment/RedirectTest.php | 2 +- .../Test/Unit/Helper/Backend/DataTest.php | 2 +- .../Test/Unit/Helper/DataTest.php | 2 +- .../Model/Directpost/Request/FactoryTest.php | 2 +- .../Model/Directpost/Response/FactoryTest.php | 2 +- .../Unit/Model/Directpost/ResponseTest.php | 2 +- .../Unit/Model/Directpost/SessionTest.php | 2 +- .../Test/Unit/Model/DirectpostTest.php | 2 +- .../Test/Unit/Model/Request/FactoryTest.php | 2 +- .../Test/Unit/Model/Response/FactoryTest.php | 2 +- .../Unit/Model/TransactionServiceTest.php | 2 +- .../AddFieldsToResponseObserverTest.php | 2 +- app/code/Magento/Authorizenet/composer.json | 5 +- .../Magento/Authorizenet/etc/adminhtml/di.xml | 2 +- .../Authorizenet/etc/adminhtml/events.xml | 2 +- .../Authorizenet/etc/adminhtml/routes.xml | 2 +- .../Authorizenet/etc/adminhtml/system.xml | 2 +- app/code/Magento/Authorizenet/etc/config.xml | 4 +- app/code/Magento/Authorizenet/etc/di.xml | 12 +- .../Magento/Authorizenet/etc/frontend/di.xml | 2 +- .../Authorizenet/etc/frontend/events.xml | 2 +- .../Authorizenet/etc/frontend/page_types.xml | 2 +- .../Authorizenet/etc/frontend/routes.xml | 4 +- .../Authorizenet/etc/frontend/sections.xml | 2 +- app/code/Magento/Authorizenet/etc/module.xml | 2 +- app/code/Magento/Authorizenet/i18n/en_US.csv | 1 + .../Magento/Authorizenet/registration.php | 2 +- ...thorizenet_directpost_payment_redirect.xml | 2 +- .../layout/sales_order_create_index.xml | 4 +- ...order_create_load_block_billing_method.xml | 4 +- .../adminhtml/layout/sales_order_view.xml | 2 +- .../templates/directpost/iframe.phtml | 2 +- .../adminhtml/templates/directpost/info.phtml | 52 +- .../order/view/info/fraud_details.phtml | 4 +- .../view/adminhtml/web/js/direct-post.js | 3 +- ...net_directpost_payment_backendresponse.xml | 2 +- ...thorizenet_directpost_payment_redirect.xml | 2 +- ...thorizenet_directpost_payment_response.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 4 +- .../view/frontend/requirejs-config.js | 2 +- .../web/js/view/payment/authorizenet.js | 42 +- .../authorizenet-directpost.js | 108 +- .../payment/authorizenet-directpost.html | 2 +- .../Magento/Backend/App/AbstractAction.php | 2 +- app/code/Magento/Backend/App/Action.php | 2 +- .../Magento/Backend/App/Action/Context.php | 2 +- .../App/Action/Plugin/Authentication.php | 2 +- .../App/Action/Plugin/MassactionKey.php | 2 +- .../Backend/App/Area/FrontNameResolver.php | 2 +- app/code/Magento/Backend/App/BackendApp.php | 2 +- .../Magento/Backend/App/BackendAppList.php | 2 +- app/code/Magento/Backend/App/Config.php | 55 +- .../Magento/Backend/App/ConfigInterface.php | 7 +- app/code/Magento/Backend/App/DefaultPath.php | 2 +- .../Backend/App/Request/PathInfoProcessor.php | 2 +- .../Backend/App/Response/Http/FileFactory.php | 2 +- app/code/Magento/Backend/App/Router.php | 2 +- .../Backend/App/Router/NoRouteHandler.php | 2 +- app/code/Magento/Backend/App/UserConfig.php | 2 +- .../Magento/Backend/Block/AbstractBlock.php | 2 +- .../Magento/Backend/Block/Admin/Formkey.php | 2 +- .../Magento/Backend/Block/AnchorRenderer.php | 90 + app/code/Magento/Backend/Block/Cache.php | 2 +- .../Backend/Block/Cache/Additional.php | 2 +- .../Block/Cache/Grid/Column/Statuses.php | 2 +- .../ProductionModeVisibilityChecker.php | 36 + .../Block/Catalog/Product/Tab/Container.php | 2 +- app/code/Magento/Backend/Block/Context.php | 2 +- app/code/Magento/Backend/Block/Dashboard.php | 2 +- .../Block/Dashboard/AbstractDashboard.php | 2 +- .../Magento/Backend/Block/Dashboard/Bar.php | 2 +- .../Backend/Block/Dashboard/Diagrams.php | 2 +- .../Magento/Backend/Block/Dashboard/Graph.php | 2 +- .../Magento/Backend/Block/Dashboard/Grid.php | 2 +- .../Magento/Backend/Block/Dashboard/Grids.php | 2 +- .../Backend/Block/Dashboard/Orders/Grid.php | 2 +- .../Magento/Backend/Block/Dashboard/Sales.php | 2 +- .../Searches/Renderer/Searchquery.php | 2 +- .../Backend/Block/Dashboard/Tab/Amounts.php | 2 +- .../Block/Dashboard/Tab/Customers/Most.php | 2 +- .../Block/Dashboard/Tab/Customers/Newest.php | 2 +- .../Backend/Block/Dashboard/Tab/Orders.php | 2 +- .../Block/Dashboard/Tab/Products/Ordered.php | 2 +- .../Block/Dashboard/Tab/Products/Viewed.php | 13 +- .../Backend/Block/Dashboard/Totals.php | 2 +- app/code/Magento/Backend/Block/Denied.php | 2 +- .../Magento/Backend/Block/GlobalSearch.php | 2 +- .../Magento/Backend/Block/Media/Uploader.php | 2 +- app/code/Magento/Backend/Block/Menu.php | 123 +- .../Magento/Backend/Block/MenuItemChecker.php | 49 + app/code/Magento/Backend/Block/Page.php | 2 +- .../Magento/Backend/Block/Page/Copyright.php | 2 +- .../Magento/Backend/Block/Page/Footer.php | 2 +- .../Magento/Backend/Block/Page/Header.php | 2 +- .../Magento/Backend/Block/Page/Notices.php | 2 +- .../Magento/Backend/Block/Page/RequireJs.php | 2 +- .../Block/Page/System/Config/Robots/Reset.php | 2 +- .../Magento/Backend/Block/Store/Switcher.php | 2 +- .../Store/Switcher/Form/Renderer/Fieldset.php | 2 +- .../Form/Renderer/Fieldset/Element.php | 2 +- .../Backend/Block/System/Account/Edit.php | 2 +- .../Block/System/Account/Edit/Form.php | 2 +- .../Backend/Block/System/Cache/Edit.php | 2 +- .../Backend/Block/System/Cache/Form.php | 2 +- .../Magento/Backend/Block/System/Design.php | 2 +- .../Backend/Block/System/Design/Edit.php | 2 +- .../Block/System/Design/Edit/Tab/General.php | 2 +- .../Backend/Block/System/Design/Edit/Tabs.php | 2 +- .../Backend/Block/System/Store/Delete.php | 2 +- .../Block/System/Store/Delete/Form.php | 2 +- .../Block/System/Store/Delete/Group.php | 2 +- .../Block/System/Store/Delete/Website.php | 2 +- .../Backend/Block/System/Store/Edit.php | 2 +- .../Block/System/Store/Edit/AbstractForm.php | 2 +- .../Block/System/Store/Edit/Form/Group.php | 2 +- .../Block/System/Store/Edit/Form/Store.php | 2 +- .../Block/System/Store/Edit/Form/Website.php | 2 +- .../Block/System/Store/Grid/Render/Group.php | 2 +- .../Block/System/Store/Grid/Render/Store.php | 2 +- .../System/Store/Grid/Render/Website.php | 2 +- .../Backend/Block/System/Store/Store.php | 2 +- app/code/Magento/Backend/Block/Template.php | 17 +- .../Backend/Block/Template/Context.php | 2 +- .../Magento/Backend/Block/Text/ListText.php | 2 +- app/code/Magento/Backend/Block/Widget.php | 2 +- .../Backend/Block/Widget/Accordion.php | 2 +- .../Backend/Block/Widget/Accordion/Item.php | 2 +- .../Backend/Block/Widget/Breadcrumbs.php | 2 +- .../Magento/Backend/Block/Widget/Button.php | 2 +- .../Block/Widget/Button/ButtonList.php | 2 +- .../Block/Widget/Button/ContextInterface.php | 2 +- .../Backend/Block/Widget/Button/Item.php | 2 +- .../Block/Widget/Button/SplitButton.php | 2 +- .../Backend/Block/Widget/Button/Toolbar.php | 2 +- .../Block/Widget/Button/Toolbar/Container.php | 2 +- .../Block/Widget/Button/ToolbarInterface.php | 2 +- .../Backend/Block/Widget/Container.php | 2 +- .../Block/Widget/ContainerInterface.php | 2 +- .../Magento/Backend/Block/Widget/Context.php | 2 +- .../Magento/Backend/Block/Widget/Form.php | 2 +- .../Backend/Block/Widget/Form/Container.php | 2 +- .../Backend/Block/Widget/Form/Element.php | 2 +- .../Block/Widget/Form/Element/Dependence.php | 2 +- .../Block/Widget/Form/Element/Gallery.php | 2 +- .../Backend/Block/Widget/Form/Generic.php | 2 +- .../Block/Widget/Form/Renderer/Element.php | 2 +- .../Block/Widget/Form/Renderer/Fieldset.php | 2 +- .../Widget/Form/Renderer/Fieldset/Element.php | 2 +- .../Magento/Backend/Block/Widget/Grid.php | 2 +- .../Backend/Block/Widget/Grid/Column.php | 2 +- .../Block/Widget/Grid/Column/Extended.php | 2 +- .../Grid/Column/Filter/AbstractFilter.php | 2 +- .../Widget/Grid/Column/Filter/Checkbox.php | 2 +- .../Widget/Grid/Column/Filter/Country.php | 2 +- .../Block/Widget/Grid/Column/Filter/Date.php | 2 +- .../Widget/Grid/Column/Filter/Datetime.php | 2 +- .../Grid/Column/Filter/FilterInterface.php | 2 +- .../Widget/Grid/Column/Filter/Massaction.php | 2 +- .../Block/Widget/Grid/Column/Filter/Price.php | 2 +- .../Block/Widget/Grid/Column/Filter/Radio.php | 2 +- .../Block/Widget/Grid/Column/Filter/Range.php | 2 +- .../Widget/Grid/Column/Filter/Select.php | 2 +- .../Grid/Column/Filter/Select/Extended.php | 2 +- .../Widget/Grid/Column/Filter/SkipList.php | 2 +- .../Block/Widget/Grid/Column/Filter/Store.php | 2 +- .../Block/Widget/Grid/Column/Filter/Text.php | 2 +- .../Block/Widget/Grid/Column/Filter/Theme.php | 2 +- .../Block/Widget/Grid/Column/Multistore.php | 2 +- .../Grid/Column/Renderer/AbstractRenderer.php | 2 +- .../Widget/Grid/Column/Renderer/Action.php | 2 +- .../Widget/Grid/Column/Renderer/Button.php | 2 +- .../Widget/Grid/Column/Renderer/Checkbox.php | 2 +- .../Column/Renderer/Checkboxes/Extended.php | 2 +- .../Widget/Grid/Column/Renderer/Concat.php | 2 +- .../Widget/Grid/Column/Renderer/Country.php | 2 +- .../Widget/Grid/Column/Renderer/Currency.php | 2 +- .../Widget/Grid/Column/Renderer/Date.php | 2 +- .../Widget/Grid/Column/Renderer/Datetime.php | 2 +- .../Grid/Column/Renderer/DraggableHandle.php | 2 +- .../Widget/Grid/Column/Renderer/Input.php | 2 +- .../Block/Widget/Grid/Column/Renderer/Ip.php | 2 +- .../Widget/Grid/Column/Renderer/Longtext.php | 2 +- .../Grid/Column/Renderer/Massaction.php | 2 +- .../Widget/Grid/Column/Renderer/Number.php | 2 +- .../Widget/Grid/Column/Renderer/Options.php | 2 +- .../Column/Renderer/Options/Converter.php | 2 +- .../Grid/Column/Renderer/Options/Extended.php | 2 +- .../Widget/Grid/Column/Renderer/Price.php | 2 +- .../Widget/Grid/Column/Renderer/Radio.php | 2 +- .../Grid/Column/Renderer/Radio/Extended.php | 2 +- .../Column/Renderer/RendererInterface.php | 2 +- .../Widget/Grid/Column/Renderer/Select.php | 2 +- .../Grid/Column/Renderer/Select/Extended.php | 2 +- .../Widget/Grid/Column/Renderer/Store.php | 2 +- .../Widget/Grid/Column/Renderer/Text.php | 72 +- .../Widget/Grid/Column/Renderer/Wrapline.php | 2 +- .../Backend/Block/Widget/Grid/ColumnSet.php | 2 +- .../Backend/Block/Widget/Grid/Container.php | 2 +- .../Backend/Block/Widget/Grid/Export.php | 2 +- .../Block/Widget/Grid/ExportInterface.php | 2 +- .../Backend/Block/Widget/Grid/Extended.php | 2 +- .../Backend/Block/Widget/Grid/Massaction.php | 7 +- .../Grid/Massaction/AbstractMassaction.php | 28 +- .../Widget/Grid/Massaction/Additional.php | 2 +- .../Block/Widget/Grid/Massaction/Extended.php | 2 +- .../Block/Widget/Grid/Massaction/Item.php | 2 +- .../Item/Additional/AdditionalInterface.php | 2 +- .../Item/Additional/DefaultAdditional.php | 2 +- .../Massaction/VisibilityCheckerInterface.php | 18 + .../Backend/Block/Widget/Grid/Serializer.php | 2 +- app/code/Magento/Backend/Block/Widget/Tab.php | 2 +- .../Backend/Block/Widget/Tab/TabInterface.php | 2 +- .../Magento/Backend/Block/Widget/Tabs.php | 2 +- .../Console/Command/AbstractCacheCommand.php | 2 +- .../Command/AbstractCacheManageCommand.php | 2 +- .../Command/AbstractCacheSetCommand.php | 2 +- .../AbstractCacheTypeManageCommand.php | 2 +- .../Console/Command/CacheCleanCommand.php | 2 +- .../Console/Command/CacheDisableCommand.php | 2 +- .../Console/Command/CacheEnableCommand.php | 2 +- .../Console/Command/CacheFlushCommand.php | 2 +- .../Console/Command/CacheStatusCommand.php | 2 +- .../Controller/Adminhtml/Ajax/Translate.php | 2 +- .../Backend/Controller/Adminhtml/Auth.php | 2 +- .../Adminhtml/Auth/DeniedIframe.php | 2 +- .../Controller/Adminhtml/Auth/DeniedJson.php | 2 +- .../Controller/Adminhtml/Auth/Login.php | 2 +- .../Controller/Adminhtml/Auth/Logout.php | 2 +- .../Adminhtml/BackendApp/Redirect.php | 2 +- .../Backend/Controller/Adminhtml/Cache.php | 2 +- .../Adminhtml/Cache/CleanImages.php | 2 +- .../Controller/Adminhtml/Cache/CleanMedia.php | 2 +- .../Adminhtml/Cache/CleanStaticFiles.php | 2 +- .../Controller/Adminhtml/Cache/FlushAll.php | 2 +- .../Adminhtml/Cache/FlushSystem.php | 2 +- .../Controller/Adminhtml/Cache/Index.php | 2 +- .../Adminhtml/Cache/MassDisable.php | 45 +- .../Controller/Adminhtml/Cache/MassEnable.php | 45 +- .../Adminhtml/Cache/MassRefresh.php | 2 +- .../Controller/Adminhtml/Dashboard.php | 2 +- .../Adminhtml/Dashboard/AjaxBlock.php | 2 +- .../Adminhtml/Dashboard/CustomersMost.php | 2 +- .../Adminhtml/Dashboard/CustomersNewest.php | 2 +- .../Controller/Adminhtml/Dashboard/Index.php | 2 +- .../Adminhtml/Dashboard/ProductsViewed.php | 2 +- .../Adminhtml/Dashboard/RefreshStatistics.php | 2 +- .../Controller/Adminhtml/Dashboard/Tunnel.php | 2 +- .../Backend/Controller/Adminhtml/Denied.php | 2 +- .../Backend/Controller/Adminhtml/Index.php | 2 +- .../Adminhtml/Index/ChangeLocale.php | 2 +- .../Adminhtml/Index/GlobalSearch.php | 2 +- .../Controller/Adminhtml/Index/Index.php | 2 +- .../Controller/Adminhtml/Noroute/Index.php | 2 +- .../Backend/Controller/Adminhtml/System.php | 2 +- .../Controller/Adminhtml/System/Account.php | 2 +- .../Adminhtml/System/Account/Index.php | 2 +- .../Adminhtml/System/Account/Save.php | 2 +- .../Controller/Adminhtml/System/Design.php | 2 +- .../Adminhtml/System/Design/Delete.php | 2 +- .../Adminhtml/System/Design/Edit.php | 2 +- .../Adminhtml/System/Design/Grid.php | 2 +- .../Adminhtml/System/Design/Index.php | 2 +- .../Adminhtml/System/Design/NewAction.php | 2 +- .../Adminhtml/System/Design/Save.php | 2 +- .../Controller/Adminhtml/System/Index.php | 2 +- .../Controller/Adminhtml/System/SetStore.php | 2 +- .../Controller/Adminhtml/System/Store.php | 2 +- .../Adminhtml/System/Store/DeleteGroup.php | 2 +- .../System/Store/DeleteGroupPost.php | 2 +- .../Adminhtml/System/Store/DeleteStore.php | 2 +- .../System/Store/DeleteStorePost.php | 2 +- .../Adminhtml/System/Store/DeleteWebsite.php | 2 +- .../System/Store/DeleteWebsitePost.php | 2 +- .../Adminhtml/System/Store/EditGroup.php | 2 +- .../Adminhtml/System/Store/EditStore.php | 2 +- .../Adminhtml/System/Store/EditWebsite.php | 2 +- .../Adminhtml/System/Store/Index.php | 16 +- .../Adminhtml/System/Store/NewGroup.php | 2 +- .../Adminhtml/System/Store/NewStore.php | 2 +- .../Adminhtml/System/Store/NewWebsite.php | 2 +- .../Adminhtml/System/Store/Save.php | 166 +- app/code/Magento/Backend/Cron/CleanCache.php | 2 +- .../Helper/Dashboard/AbstractDashboard.php | 2 +- .../Magento/Backend/Helper/Dashboard/Data.php | 2 +- .../Backend/Helper/Dashboard/Order.php | 2 +- app/code/Magento/Backend/Helper/Data.php | 2 +- app/code/Magento/Backend/Helper/Js.php | 2 +- .../Magento/Backend/Model/AdminPathConfig.php | 2 +- app/code/Magento/Backend/Model/Auth.php | 2 +- .../Auth/Credential/StorageInterface.php | 2 +- .../Magento/Backend/Model/Auth/Session.php | 2 +- .../Backend/Model/Auth/StorageInterface.php | 2 +- .../Model/Authorization/RoleLocator.php | 2 +- .../Cache/ResourceModel/Grid/Collection.php | 2 +- .../Config/SessionLifetime/BackendModel.php | 2 +- .../Magento/Backend/Model/Locale/Manager.php | 2 +- .../Magento/Backend/Model/Locale/Resolver.php | 2 +- app/code/Magento/Backend/Model/Menu.php | 111 +- .../Backend/Model/Menu/AbstractDirector.php | 2 +- .../Magento/Backend/Model/Menu/Builder.php | 2 +- .../Model/Menu/Builder/AbstractCommand.php | 2 +- .../Model/Menu/Builder/Command/Add.php | 2 +- .../Model/Menu/Builder/Command/Remove.php | 2 +- .../Model/Menu/Builder/Command/Update.php | 2 +- .../Model/Menu/Builder/CommandFactory.php | 2 +- .../Magento/Backend/Model/Menu/Config.php | 2 +- .../Backend/Model/Menu/Config/Converter.php | 8 +- .../Backend/Model/Menu/Config/Menu/Dom.php | 2 +- .../Backend/Model/Menu/Config/Reader.php | 2 +- .../Model/Menu/Config/SchemaLocator.php | 2 +- .../Backend/Model/Menu/Director/Director.php | 2 +- .../Backend/Model/Menu/Filter/Iterator.php | 2 +- app/code/Magento/Backend/Model/Menu/Item.php | 112 +- .../Backend/Model/Menu/Item/Factory.php | 2 +- .../Backend/Model/Menu/Item/Validator.php | 2 +- .../Magento/Backend/Model/Menu/Iterator.php | 2 +- .../Backend/Model/ResourceModel/Translate.php | 2 +- .../Magento/Backend/Model/Search/Customer.php | 2 +- .../Magento/Backend/Model/Search/Order.php | 2 +- app/code/Magento/Backend/Model/Session.php | 2 +- .../Backend/Model/Session/AdminConfig.php | 2 +- .../Magento/Backend/Model/Session/Quote.php | 9 +- .../Backend/Model/Setup/MenuBuilder.php | 2 +- .../Backend/Model/Translate/Inline/Config.php | 2 +- app/code/Magento/Backend/Model/Url.php | 15 +- .../Backend/Model/Url/ScopeResolver.php | 2 +- .../Magento/Backend/Model/UrlInterface.php | 2 +- .../Backend/Model/View/Layout/Builder.php | 2 +- .../Model/View/Layout/ConditionInterface.php | 38 + .../Model/View/Layout/ConditionPool.php | 60 + .../Backend/Model/View/Layout/Filter.php | 52 + .../Backend/Model/View/Layout/Filter/Acl.php | 57 +- .../Model/View/Layout/Filter/Condition.php | 67 + .../Model/View/Layout/FilterInterface.php | 38 + .../Model/View/Layout/GeneratorPool.php | 21 +- .../Model/View/Layout/Reader/Block.php | 2 +- .../Model/View/Layout/StructureManager.php | 46 + .../Backend/Model/View/Page/Builder.php | 2 +- .../Backend/Model/View/Result/Forward.php | 2 +- .../Backend/Model/View/Result/Page.php | 39 +- .../Backend/Model/View/Result/Redirect.php | 2 +- .../Model/View/Result/RedirectFactory.php | 2 +- .../Model/Widget/Grid/AbstractTotals.php | 2 +- .../Backend/Model/Widget/Grid/Parser.php | 2 +- .../Widget/Grid/Row/GeneratorInterface.php | 2 +- .../Model/Widget/Grid/Row/UrlGenerator.php | 2 +- .../Widget/Grid/Row/UrlGeneratorFactory.php | 2 +- .../Model/Widget/Grid/Row/UrlGeneratorId.php | 2 +- .../Backend/Model/Widget/Grid/SubTotals.php | 2 +- .../Backend/Model/Widget/Grid/Totals.php | 2 +- .../Model/Widget/Grid/TotalsInterface.php | 2 +- .../Backend/Service/V1/ModuleService.php | 2 +- .../Service/V1/ModuleServiceInterface.php | 2 +- .../Backend/Setup/ConfigOptionsList.php | 2 +- .../App/Action/Plugin/AuthenticationTest.php | 2 +- .../App/Action/Plugin/MassactionKeyTest.php | 2 +- .../Test/Unit/App/Action/Stub/ActionStub.php | 2 +- .../Unit/App/Area/FrontNameResolverTest.php | 2 +- .../Area/Request/PathInfoProcessorTest.php | 2 +- .../Backend/Test/Unit/App/ConfigTest.php | 96 +- .../App/Response/Http/FileFactoryTest.php | 2 +- .../Unit/App/Router/NoRouteHandlerTest.php | 2 +- .../Backend/Test/Unit/App/UserConfigTest.php | 2 +- .../Test/Unit/Block/AnchorRendererTest.php | 134 + .../Test/Unit/Block/Cache/AdditionalTest.php | 2 +- .../Test/Unit/Block/MenuItemCheckerTest.php | 85 + .../Backend/Test/Unit/Block/MenuTest.php | 146 + .../Page/System/Config/Robots/ResetTest.php | 2 +- .../Test/Unit/Block/Store/SwitcherTest.php | 2 +- .../Unit/Block/Widget/Button/SplitTest.php | 2 +- .../Test/Unit/Block/Widget/ButtonTest.php | 2 +- .../Unit/Block/Widget/Form/ContainerTest.php | 2 +- .../Test/Unit/Block/Widget/FormTest.php | 2 +- .../Widget/Grid/Column/Filter/DateTest.php | 2 +- .../Grid/Column/Filter/DatetimeTest.php | 2 +- .../Widget/Grid/Column/Filter/StoreTest.php | 2 +- .../Widget/Grid/Column/Filter/TextTest.php | 2 +- .../Widget/Grid/Column/MultistoreTest.php | 2 +- .../Column/Renderer/AbstractRendererTest.php | 2 +- .../Grid/Column/Renderer/ConcatTest.php | 2 +- .../Grid/Column/Renderer/CurrencyTest.php | 2 +- .../Column/Renderer/Radio/ExtendedTest.php | 2 +- .../Widget/Grid/Column/Renderer/RadioTest.php | 2 +- .../Unit/Block/Widget/Grid/ColumnSetTest.php | 2 +- .../Unit/Block/Widget/Grid/ColumnTest.php | 2 +- .../Unit/Block/Widget/Grid/ExtendedTest.php | 2 +- .../Widget/Grid/Massaction/ExtendedTest.php | 2 +- .../Unit/Block/Widget/Grid/MassactionTest.php | 202 +- .../Unit/Block/Widget/Grid/SerializerTest.php | 2 +- .../Test/Unit/Block/Widget/TabTest.php | 2 +- .../Command/AbstractCacheCommandTest.php | 2 +- .../AbstractCacheManageCommandTest.php | 2 +- .../Command/AbstractCacheSetCommandTest.php | 2 +- .../Console/Command/CacheCleanCommandTest.php | 2 +- .../Command/CacheDisableCommandTest.php | 2 +- .../Command/CacheEnableCommandTest.php | 2 +- .../Console/Command/CacheFlushCommandTest.php | 2 +- .../Command/CacheStatusCommandTest.php | 2 +- .../Adminhtml/Cache/CleanMediaTest.php | 2 +- .../Adminhtml/Cache/CleanStaticFilesTest.php | 2 +- .../Adminhtml/Cache/MassDisableTest.php | 224 + .../Adminhtml/Cache/MassEnableTest.php | 224 + .../Adminhtml/Dashboard/AbstractTestCase.php | 2 +- .../Adminhtml/Dashboard/CustomersMostTest.php | 2 +- .../Dashboard/CustomersNewestTest.php | 2 +- .../Dashboard/ProductsViewedTest.php | 2 +- .../Dashboard/RefreshStatisticsTest.php | 2 +- .../Adminhtml/Dashboard/TunnelTest.php | 2 +- .../Adminhtml/System/Account/SaveTest.php | 2 +- .../Adminhtml/System/Store/IndexTest.php | 97 + .../Backend/Test/Unit/Cron/CleanCacheTest.php | 2 +- .../Backend/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Model/AdminPathConfigTest.php | 2 +- .../Test/Unit/Model/Auth/SessionTest.php | 2 +- .../Backend/Test/Unit/Model/AuthTest.php | 2 +- .../Model/Authorization/RoleLocatorTest.php | 2 +- .../SessionLifetime/BackendModelTest.php | 2 +- .../Test/Unit/Model/Locale/ManagerTest.php | 2 +- .../Menu/Builder/AbstractCommandTest.php | 2 +- .../Model/Menu/Builder/Command/AddTest.php | 2 +- .../Model/Menu/Builder/Command/RemoveTest.php | 2 +- .../Model/Menu/Builder/Command/UpdateTest.php | 2 +- .../Test/Unit/Model/Menu/BuilderTest.php | 63 +- .../Unit/Model/Menu/Config/ConverterTest.php | 2 +- .../Model/Menu/Config/SchemaLocatorTest.php | 2 +- .../Test/Unit/Model/Menu/Config/XsdTest.php | 2 +- .../Config/_files/invalidMenuXmlArray.php | 2 +- .../Model/Menu/Config/_files/invalid_menu.xml | 2 +- .../Model/Menu/Config/_files/valid_menu.xml | 2 +- .../Test/Unit/Model/Menu/ConfigTest.php | 129 +- .../Unit/Model/Menu/Director/DirectorTest.php | 2 +- .../Unit/Model/Menu/Filter/IteratorTest.php | 110 +- .../Unit/Model/Menu/Item/ValidatorTest.php | 2 +- .../Backend/Test/Unit/Model/Menu/ItemTest.php | 114 +- .../Test/Unit/Model/MenuBuilderTest.php | 2 +- .../Backend/Test/Unit/Model/MenuTest.php | 118 +- .../Unit/Model/Session/AdminConfigTest.php | 2 +- .../Test/Unit/Model/Session/QuoteTest.php | 39 +- .../Model/Translate/Inline/ConfigTest.php | 2 +- .../Backend/Test/Unit/Model/UrlTest.php | 71 +- .../Model/View/Layout/ConditionPoolTest.php | 65 + .../Unit/Model/View/Layout/Filter/AclTest.php | 55 +- .../View/Layout/Filter/ConditionTest.php | 139 + .../Unit/Model/View/Layout/FilterTest.php | 65 + .../View/Layout/StructureManagerTest.php | 87 + .../Test/Unit/Model/View/Result/PageTest.php | 2 +- .../Unit/Model/View/Result/RedirectTest.php | 2 +- .../Model/Widget/Grid/AbstractTotalsTest.php | 2 +- .../Unit/Model/Widget/Grid/ParserTest.php | 2 +- .../Widget/Grid/Row/UrlGeneratorTest.php | 2 +- .../Unit/Model/Widget/Grid/SubTotalsTest.php | 2 +- .../Unit/Model/Widget/Grid/TotalsTest.php | 2 +- .../_files/menu_item_constructor_data.php | 136 + .../Test/Unit/Model/_files/menu_item_data.php | 121 + .../Test/Unit/Model/_files/menu_merged.php | 2 +- .../Test/Unit/Model/_files/menu_merged.xml | 2 +- .../Test/Unit/Setup/ConfigOptionsListTest.php | 2 +- app/code/Magento/Backend/composer.json | 2 +- app/code/Magento/Backend/etc/acl.xml | 2 +- app/code/Magento/Backend/etc/adminhtml/di.xml | 10 +- .../Magento/Backend/etc/adminhtml/menu.xml | 2 +- .../Magento/Backend/etc/adminhtml/routes.xml | 4 +- .../Magento/Backend/etc/adminhtml/system.xml | 8 +- app/code/Magento/Backend/etc/config.xml | 5 +- app/code/Magento/Backend/etc/crontab.xml | 2 +- app/code/Magento/Backend/etc/di.xml | 60 +- app/code/Magento/Backend/etc/menu.xsd | 4 +- app/code/Magento/Backend/etc/module.xml | 2 +- app/code/Magento/Backend/etc/webapi.xml | 2 +- app/code/Magento/Backend/i18n/en_US.csv | 2 +- app/code/Magento/Backend/registration.php | 2 +- .../view/adminhtml/layout/admin_login.xml | 2 +- .../adminhtml/layout/adminhtml_auth_login.xml | 2 +- .../layout/adminhtml_cache_block.xml | 6 +- .../layout/adminhtml_cache_index.xml | 2 +- .../adminhtml_dashboard_customersmost.xml | 2 +- .../adminhtml_dashboard_customersnewest.xml | 2 +- .../layout/adminhtml_dashboard_index.xml | 2 +- .../adminhtml_dashboard_productsviewed.xml | 2 +- .../adminhtml/layout/adminhtml_denied.xml | 2 +- .../adminhtml/layout/adminhtml_noroute.xml | 2 +- .../layout/adminhtml_system_account_index.xml | 2 +- .../layout/adminhtml_system_design_edit.xml | 2 +- .../layout/adminhtml_system_design_grid.xml | 2 +- .../adminhtml_system_design_grid_block.xml | 2 +- .../layout/adminhtml_system_design_index.xml | 2 +- .../adminhtml_system_store_grid_block.xml | 2 +- .../layout/adminhtml_system_store_index.xml | 2 +- .../Backend/view/adminhtml/layout/default.xml | 2 +- .../Backend/view/adminhtml/layout/editor.xml | 2 +- .../Backend/view/adminhtml/layout/empty.xml | 2 +- .../Backend/view/adminhtml/layout/formkey.xml | 2 +- .../view/adminhtml/layout/overlay_popup.xml | 2 +- .../Backend/view/adminhtml/layout/popup.xml | 2 +- .../view/adminhtml/requirejs-config.js | 4 +- .../templates/admin/access_denied.phtml | 2 +- .../adminhtml/templates/admin/formkey.phtml | 2 +- .../adminhtml/templates/admin/login.phtml | 2 +- .../templates/admin/login_buttons.phtml | 2 +- .../templates/admin/overlay_popup.phtml | 2 +- .../view/adminhtml/templates/admin/page.phtml | 2 +- .../adminhtml/templates/dashboard/graph.phtml | 2 +- .../templates/dashboard/graph/disabled.phtml | 2 +- .../adminhtml/templates/dashboard/grid.phtml | 2 +- .../adminhtml/templates/dashboard/index.phtml | 2 +- .../templates/dashboard/salebar.phtml | 2 +- .../templates/dashboard/searches.phtml | 2 +- .../templates/dashboard/store/switcher.phtml | 2 +- .../templates/dashboard/totalbar.phtml | 2 +- .../totalbar/refreshstatistics.phtml | 16 +- .../adminhtml/templates/media/uploader.phtml | 2 +- .../view/adminhtml/templates/menu.phtml | 2 +- .../adminhtml/templates/page/copyright.phtml | 2 +- .../adminhtml/templates/page/footer.phtml | 2 +- .../adminhtml/templates/page/header.phtml | 2 +- .../templates/page/js/calendar.phtml | 2 +- .../templates/page/js/components.phtml | 2 +- .../templates/page/js/require_js.phtml | 2 +- .../adminhtml/templates/page/notices.phtml | 2 +- .../adminhtml/templates/page/report.phtml | 4 +- .../adminhtml/templates/pageactions.phtml | 2 +- .../adminhtml/templates/store/switcher.phtml | 2 +- .../switcher/form/renderer/fieldset.phtml | 2 +- .../form/renderer/fieldset/element.phtml | 2 +- .../templates/system/autocomplete.phtml | 2 +- .../templates/system/cache/additional.phtml | 2 +- .../templates/system/cache/edit.phtml | 2 +- .../templates/system/design/edit.phtml | 2 +- .../templates/system/design/index.phtml | 2 +- .../adminhtml/templates/system/search.phtml | 2 +- .../system/shipping/applicable_country.phtml | 2 +- .../templates/widget/accordion.phtml | 2 +- .../templates/widget/breadcrumbs.phtml | 2 +- .../adminhtml/templates/widget/button.phtml | 2 +- .../templates/widget/button/split.phtml | 2 +- .../adminhtml/templates/widget/form.phtml | 2 +- .../templates/widget/form/container.phtml | 2 +- .../templates/widget/form/element.phtml | 2 +- .../widget/form/element/gallery.phtml | 2 +- .../widget/form/renderer/element.phtml | 2 +- .../widget/form/renderer/fieldset.phtml | 2 +- .../form/renderer/fieldset/element.phtml | 2 +- .../adminhtml/templates/widget/grid.phtml | 2 +- .../templates/widget/grid/column_set.phtml | 2 +- .../templates/widget/grid/container.phtml | 2 +- .../widget/grid/container/empty.phtml | 2 +- .../templates/widget/grid/export.phtml | 4 +- .../templates/widget/grid/extended.phtml | 2 +- .../templates/widget/grid/massaction.phtml | 2 +- .../widget/grid/massaction_extended.phtml | 2 +- .../templates/widget/grid/serializer.phtml | 2 +- .../adminhtml/templates/widget/tabs.phtml | 2 +- .../templates/widget/tabshoriz.phtml | 2 +- .../adminhtml/templates/widget/tabsleft.phtml | 2 +- .../templates/widget/view/container.phtml | 2 +- .../ui_component/design_config_form.xml | 2 +- .../ui_component/design_config_listing.xml | 2 +- .../view/adminhtml/web/js/bootstrap/editor.js | 9 +- .../view/adminhtml/web/js/media-uploader.js | 2 +- .../dynamic-rows/cells/action-delete.html | 2 +- .../web/template/dynamic-rows/grid.html | 2 +- .../element/helper/fallback-reset-link.html | 2 +- .../Magento/Backup/Block/Adminhtml/Backup.php | 2 +- .../Backup/Block/Adminhtml/Dialogs.php | 2 +- .../Grid/Column/Renderer/Download.php | 2 +- .../Block/Adminhtml/Grid/Column/Rollback.php | 2 +- .../Backup/Controller/Adminhtml/Index.php | 2 +- .../Controller/Adminhtml/Index/Create.php | 2 +- .../Controller/Adminhtml/Index/Download.php | 2 +- .../Controller/Adminhtml/Index/Grid.php | 9 +- .../Controller/Adminhtml/Index/Index.php | 2 +- .../Controller/Adminhtml/Index/MassDelete.php | 2 +- .../Controller/Adminhtml/Index/Rollback.php | 2 +- app/code/Magento/Backup/Cron/SystemBackup.php | 2 +- app/code/Magento/Backup/Helper/Data.php | 2 +- app/code/Magento/Backup/Model/Backup.php | 2 +- .../Magento/Backup/Model/BackupFactory.php | 2 +- .../Backup/Model/Config/Backend/Cron.php | 2 +- .../Backup/Model/Config/Source/Type.php | 2 +- app/code/Magento/Backup/Model/Db.php | 2 +- .../Magento/Backup/Model/Fs/Collection.php | 2 +- .../Magento/Backup/Model/Grid/Options.php | 2 +- .../Magento/Backup/Model/ResourceModel/Db.php | 2 +- .../Backup/Model/ResourceModel/Helper.php | 2 +- .../Adminhtml/Index/DownloadTest.php | 2 +- .../Adminhtml/Index/RollbackTest.php | 2 +- .../Test/Unit/Cron/SystemBackupTest.php | 2 +- .../Backup/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Model/BackupFactoryTest.php | 2 +- .../Backup/Test/Unit/Model/BackupTest.php | 2 +- .../Test/Unit/Model/Fs/CollectionTest.php | 2 +- app/code/Magento/Backup/composer.json | 2 +- app/code/Magento/Backup/etc/acl.xml | 2 +- .../Magento/Backup/etc/adminhtml/menu.xml | 2 +- .../Magento/Backup/etc/adminhtml/routes.xml | 2 +- .../Magento/Backup/etc/adminhtml/system.xml | 2 +- app/code/Magento/Backup/etc/crontab.xml | 2 +- app/code/Magento/Backup/etc/di.xml | 2 +- app/code/Magento/Backup/etc/module.xml | 2 +- app/code/Magento/Backup/registration.php | 2 +- .../adminhtml/layout/backup_index_block.xml | 2 +- .../adminhtml/layout/backup_index_grid.xml | 2 +- .../adminhtml/layout/backup_index_index.xml | 2 +- .../adminhtml/templates/backup/dialogs.phtml | 2 +- .../adminhtml/templates/backup/left.phtml | 2 +- .../adminhtml/templates/backup/list.phtml | 2 +- .../Block/Adminhtml/Form/Field/CcTypes.php | 2 +- .../Block/Adminhtml/Form/Field/Countries.php | 2 +- .../Form/Field/CountryCreditCard.php | 2 +- .../Braintree/Block/Customer/CardRenderer.php | 2 +- .../Customer/PayPal/VaultTokenRenderer.php | 2 +- app/code/Magento/Braintree/Block/Form.php | 2 +- app/code/Magento/Braintree/Block/Info.php | 2 +- app/code/Magento/Braintree/Block/Payment.php | 2 +- .../Magento/Braintree/Block/Paypal/Button.php | 2 +- .../Block/Paypal/Checkout/Review.php | 2 +- .../Controller/Adminhtml/Payment/GetNonce.php | 2 +- .../Controller/Adminhtml/Report/Index.php | 2 +- .../Braintree/Controller/Payment/GetNonce.php | 2 +- .../Controller/Paypal/AbstractAction.php | 2 +- .../Controller/Paypal/PlaceOrder.php | 17 +- .../Braintree/Controller/Paypal/Review.php | 2 +- .../Controller/Paypal/SaveShippingMethod.php | 2 +- .../Command/CaptureStrategyCommand.php | 2 +- .../Command/GetPaymentNonceCommand.php | 2 +- .../Gateway/Config/CanVoidHandler.php | 2 +- .../Braintree/Gateway/Config/Config.php | 31 +- .../Gateway/Config/PayPal/Config.php | 2 +- .../Gateway/Helper/SubjectReader.php | 2 +- .../Http/Client/AbstractTransaction.php | 2 +- .../Gateway/Http/Client/TransactionRefund.php | 2 +- .../Gateway/Http/Client/TransactionSale.php | 2 +- .../Client/TransactionSubmitForSettlement.php | 2 +- .../Gateway/Http/Client/TransactionVoid.php | 2 +- .../Gateway/Http/TransferFactory.php | 2 +- .../Gateway/Request/AddressDataBuilder.php | 2 +- .../Gateway/Request/CaptureDataBuilder.php | 2 +- .../Gateway/Request/ChannelDataBuilder.php | 2 +- .../Gateway/Request/CustomerDataBuilder.php | 2 +- .../Gateway/Request/DescriptorDataBuilder.php | 2 +- .../Request/KountPaymentDataBuilder.php | 2 +- .../Request/PayPal/DeviceDataBuilder.php | 2 +- .../Request/PayPal/VaultDataBuilder.php | 2 +- .../Gateway/Request/PaymentDataBuilder.php | 2 +- .../Gateway/Request/RefundDataBuilder.php | 2 +- .../Gateway/Request/SettlementDataBuilder.php | 2 +- .../Request/ThreeDSecureDataBuilder.php | 2 +- .../Request/VaultCaptureDataBuilder.php | 2 +- .../Gateway/Request/VaultDataBuilder.php | 2 +- .../Gateway/Request/VoidDataBuilder.php | 2 +- .../Gateway/Response/CardDetailsHandler.php | 2 +- .../Response/PayPal/VaultDetailsHandler.php | 2 +- .../Gateway/Response/PayPalDetailsHandler.php | 2 +- .../Response/PaymentDetailsHandler.php | 2 +- .../Gateway/Response/RefundHandler.php | 2 +- .../Gateway/Response/RiskDataHandler.php | 2 +- .../Response/ThreeDSecureDetailsHandler.php | 2 +- .../Gateway/Response/TransactionIdHandler.php | 2 +- .../Gateway/Response/VaultDetailsHandler.php | 18 +- .../Gateway/Response/VoidHandler.php | 2 +- .../Validator/GeneralResponseValidator.php | 2 +- .../PaymentNonceResponseValidator.php | 2 +- .../Gateway/Validator/ResponseValidator.php | 2 +- app/code/Magento/Braintree/Helper/CcType.php | 2 +- app/code/Magento/Braintree/Helper/Country.php | 17 +- .../Model/Adapter/BraintreeAdapter.php | 2 +- .../Model/Adapter/BraintreeSearchAdapter.php | 2 +- .../Model/Adminhtml/Source/CcType.php | 2 +- .../Model/Adminhtml/Source/Environment.php | 2 +- .../Model/Adminhtml/Source/PaymentAction.php | 2 +- .../Model/Adminhtml/System/Config/Country.php | 2 +- .../System/Config/CountryCreditCard.php | 18 +- .../Model/Paypal/Helper/AbstractHelper.php | 2 +- .../Model/Paypal/Helper/OrderPlace.php | 2 +- .../Model/Paypal/Helper/QuoteUpdater.php | 2 +- .../Paypal/Helper/ShippingMethodUpdater.php | 2 +- .../ConditionAppliers/ApplierInterface.php | 2 +- .../Report/ConditionAppliers/AppliersPool.php | 2 +- .../ConditionAppliers/MultipleValue.php | 2 +- .../Model/Report/ConditionAppliers/Range.php | 2 +- .../Model/Report/ConditionAppliers/Text.php | 2 +- .../Braintree/Model/Report/FilterMapper.php | 2 +- .../Model/Report/Row/TransactionMap.php | 2 +- .../Model/Report/TransactionsCollection.php | 2 +- .../PayPal/TokenUiComponentProvider.php | 84 + .../Ui/Adminhtml/TokenUiComponentProvider.php | 3 +- .../Braintree/Model/Ui/ConfigProvider.php | 4 +- .../Model/Ui/PayPal/ConfigProvider.php | 2 +- .../Ui/PayPal/TokenUiComponentProvider.php | 2 +- .../Model/Ui/TokenUiComponentProvider.php | 2 +- .../Braintree/Observer/AddPaypalShortcuts.php | 2 +- .../Braintree/Observer/DataAssignObserver.php | 2 +- .../Magento/Braintree/Setup/UpgradeData.php | 83 + .../Braintree/Test/Unit/Block/FormTest.php | 2 +- .../Unit/Controller/Payment/GetNonceTest.php | 2 +- .../Unit/Controller/Paypal/PlaceOrderTest.php | 13 +- .../Unit/Controller/Paypal/ReviewTest.php | 2 +- .../Paypal/SaveShippingMethodTest.php | 2 +- .../Command/CaptureStrategyCommandTest.php | 2 +- .../Command/GetPaymentNonceCommandTest.php | 2 +- .../Gateway/Config/CanVoidHandlerTest.php | 2 +- .../Test/Unit/Gateway/Config/ConfigTest.php | 48 +- .../Unit/Gateway/Helper/SubjectReaderTest.php | 2 +- .../Http/Client/TransactionSaleTest.php | 2 +- .../TransactionSubmitForSettlementTest.php | 2 +- .../Unit/Gateway/Http/TransferFactoryTest.php | 2 +- .../Request/AddressDataBuilderTest.php | 2 +- .../Request/CaptureDataBuilderTest.php | 2 +- .../Request/ChannelDataBuilderTest.php | 2 +- .../Request/CustomerDataBuilderTest.php | 2 +- .../Request/DescriptorDataBuilderTest.php | 2 +- .../Request/KountPaymentDataBuilderTest.php | 2 +- .../Request/PayPal/DeviceDataBuilderTest.php | 2 +- .../Request/PayPal/VaultDataBuilderTest.php | 2 +- .../Request/PaymentDataBuilderTest.php | 2 +- .../Gateway/Request/RefundDataBuilderTest.php | 2 +- .../Request/SettlementDataBuilderTest.php | 2 +- .../Request/ThreeDSecureDataBuilderTest.php | 2 +- .../Request/VaultCaptureDataBuilderTest.php | 2 +- .../Gateway/Request/VaultDataBuilderTest.php | 2 +- .../Response/CardDetailsHandlerTest.php | 2 +- .../PayPal/VaultDetailsHandlerTest.php | 2 +- .../Response/PayPalDetailsHandlerTest.php | 2 +- .../Response/PaymentDetailsHandlerTest.php | 2 +- .../Gateway/Response/RiskDataHandlerTest.php | 2 +- .../ThreeDSecureDetailsHandlerTest.php | 2 +- .../Response/TransactionIdHandlerTest.php | 2 +- .../Response/VaultDetailsHandlerTest.php | 13 +- .../Unit/Gateway/Response/VoidHandlerTest.php | 2 +- .../GeneralResponseValidatorTest.php | 2 +- .../PaymentNonceResponseValidatorTest.php | 2 +- .../Validator/ResponseValidatorTest.php | 2 +- .../Braintree/Test/Unit/Helper/CcTypeTest.php | 2 +- .../Test/Unit/Helper/CountryTest.php | 2 +- .../System/Config/CountryCreditCardTest.php | 98 +- .../Adminhtml/System/Config/CountryTest.php | 2 +- .../Model/Paypal/Helper/OrderPlaceTest.php | 2 +- .../Model/Paypal/Helper/QuoteUpdaterTest.php | 2 +- .../Helper/ShippingMethodUpdaterTest.php | 2 +- .../Model/Report/BraintreeSearchNodeStub.php | 2 +- .../Model/Report/BraintreeTransactionStub.php | 2 +- .../Unit/Model/Report/FilterMapperTest.php | 2 +- .../Unit/Model/Report/TransactionMapTest.php | 2 +- .../Report/TransactionsCollectionTest.php | 2 +- .../PayPal/TokenUiComponentProviderTest.php | 114 + .../TokenUiComponentProviderTest.php | 9 +- .../Test/Unit/Model/Ui/ConfigProviderTest.php | 2 +- .../Model/Ui/PayPal/ConfigProviderTest.php | 2 +- .../PayPal/TokenUiComponentProviderTest.php | 2 +- .../Unit/Observer/AddPaypalShortcutsTest.php | 2 +- .../Unit/Observer/DataAssignObserverTest.php | 2 +- .../Report/Filters/Type/DateRangeTest.php | 2 +- .../Column/CheckColumnOptionSourceTest.php | 2 +- .../Report/Filters/Type/DateRange.php | 2 +- .../Report/Listing/Column/PaymentType.php | 2 +- .../Report/Listing/Column/Status.php | 2 +- .../Report/Listing/Column/TransactionType.php | 2 +- app/code/Magento/Braintree/composer.json | 2 +- app/code/Magento/Braintree/etc/acl.xml | 2 +- .../Magento/Braintree/etc/adminhtml/di.xml | 3 +- .../Magento/Braintree/etc/adminhtml/menu.xml | 2 +- .../Braintree/etc/adminhtml/routes.xml | 2 +- .../Braintree/etc/adminhtml/system.xml | 2 +- app/code/Magento/Braintree/etc/config.xml | 5 +- app/code/Magento/Braintree/etc/di.xml | 37 +- app/code/Magento/Braintree/etc/events.xml | 2 +- .../Magento/Braintree/etc/frontend/di.xml | 2 +- .../Magento/Braintree/etc/frontend/events.xml | 2 +- .../Magento/Braintree/etc/frontend/routes.xml | 2 +- .../Braintree/etc/frontend/sections.xml | 2 +- app/code/Magento/Braintree/etc/module.xml | 4 +- app/code/Magento/Braintree/registration.php | 2 +- .../layout/adminhtml_system_config_edit.xml | 4 +- .../layout/braintree_report_index.xml | 4 +- .../layout/sales_order_create_index.xml | 6 +- ...order_create_load_block_billing_method.xml | 8 +- .../view/adminhtml/templates/form/cc.phtml | 2 +- .../templates/form/paypal/vault.phtml | 30 + .../view/adminhtml/templates/form/vault.phtml | 5 +- .../adminhtml/templates/grid/tooltip.phtml | 2 +- .../adminhtml/templates/payment/script.phtml | 4 +- .../ui_component/braintree_report.xml | 2 +- .../view/adminhtml/web/js/braintree.js | 2 +- .../adminhtml/web/js/grid/filters/status.html | 2 +- .../view/adminhtml/web/js/grid/provider.js | 2 +- .../Braintree/view/adminhtml/web/js/vault.js | 38 +- .../Braintree/view/adminhtml/web/styles.css | 4 +- .../Braintree/view/base/web/js/validator.js | 2 +- .../layout/braintree_paypal_review.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../layout/vault_cards_listaction.xml | 2 +- .../view/frontend/requirejs-config.js | 2 +- .../frontend/templates/paypal/button.phtml | 2 +- .../templates/paypal/vault_token.phtml | 2 +- .../view/frontend/web/js/paypal/button.js | 2 +- .../frontend/web/js/paypal/form-builder.js | 2 +- .../frontend/web/js/view/payment/3d-secure.js | 2 +- .../frontend/web/js/view/payment/adapter.js | 2 +- .../frontend/web/js/view/payment/braintree.js | 2 +- .../view/payment/method-renderer/cc-form.js | 2 +- .../payment/method-renderer/hosted-fields.js | 2 +- .../payment/method-renderer/paypal-vault.js | 2 +- .../js/view/payment/method-renderer/paypal.js | 24 +- .../js/view/payment/method-renderer/vault.js | 2 +- .../web/js/view/payment/validator-handler.js | 2 +- .../frontend/web/template/payment/form.html | 6 +- .../frontend/web/template/payment/paypal.html | 8 +- .../web/template/payment/paypal/vault.html | 4 +- .../Bundle/Api/Data/BundleOptionInterface.php | 2 +- .../Magento/Bundle/Api/Data/LinkInterface.php | 2 +- .../Bundle/Api/Data/OptionInterface.php | 2 +- .../Bundle/Api/Data/OptionTypeInterface.php | 2 +- .../Api/ProductLinkManagementInterface.php | 2 +- .../Api/ProductOptionManagementInterface.php | 2 +- .../Api/ProductOptionRepositoryInterface.php | 2 +- .../Api/ProductOptionTypeListInterface.php | 2 +- .../Product/Composite/Fieldset/Bundle.php | 2 +- .../Fieldset/Options/Type/Checkbox.php | 2 +- .../Composite/Fieldset/Options/Type/Multi.php | 2 +- .../Composite/Fieldset/Options/Type/Radio.php | 2 +- .../Fieldset/Options/Type/Select.php | 2 +- .../Catalog/Product/Edit/Tab/Attributes.php | 2 +- .../Product/Edit/Tab/Attributes/Extend.php | 2 +- .../Product/Edit/Tab/Attributes/Special.php | 2 +- .../Catalog/Product/Edit/Tab/Bundle.php | 2 +- .../Product/Edit/Tab/Bundle/Option.php | 2 +- .../Product/Edit/Tab/Bundle/Option/Search.php | 2 +- .../Edit/Tab/Bundle/Option/Search/Grid.php | 2 +- .../Edit/Tab/Bundle/Option/Selection.php | 2 +- .../Adminhtml/Catalog/Product/Edit/Tabs.php | 2 +- .../Block/Adminhtml/Order/Create/Sidebar.php | 2 +- .../Adminhtml/Sales/Order/Items/Renderer.php | 34 +- .../Sales/Order/View/Items/Renderer.php | 46 +- .../Bundle/Block/Catalog/Product/Price.php | 2 +- .../Catalog/Product/View/Type/Bundle.php | 33 +- .../Product/View/Type/Bundle/Option.php | 2 +- .../View/Type/Bundle/Option/Checkbox.php | 2 +- .../Product/View/Type/Bundle/Option/Multi.php | 2 +- .../Product/View/Type/Bundle/Option/Radio.php | 2 +- .../View/Type/Bundle/Option/Select.php | 2 +- .../Block/Checkout/Cart/Item/Renderer.php | 2 +- .../Block/Sales/Order/Items/Renderer.php | 32 +- .../Product/Edit/AddAttributeToTemplate.php | 2 +- .../Bundle/Product/Edit/AlertsPriceGrid.php | 2 +- .../Bundle/Product/Edit/AlertsStockGrid.php | 2 +- .../Bundle/Product/Edit/Categories.php | 2 +- .../Bundle/Product/Edit/Crosssell.php | 2 +- .../Bundle/Product/Edit/CrosssellGrid.php | 2 +- .../Bundle/Product/Edit/CustomOptions.php | 2 +- .../Bundle/Product/Edit/Duplicate.php | 2 +- .../Adminhtml/Bundle/Product/Edit/Edit.php | 2 +- .../Adminhtml/Bundle/Product/Edit/Form.php | 2 +- .../Adminhtml/Bundle/Product/Edit/Grid.php | 2 +- .../Bundle/Product/Edit/GridOnly.php | 2 +- .../Adminhtml/Bundle/Product/Edit/Index.php | 2 +- .../Bundle/Product/Edit/MassDelete.php | 2 +- .../Bundle/Product/Edit/MassStatus.php | 2 +- .../Bundle/Product/Edit/NewAction.php | 2 +- .../Adminhtml/Bundle/Product/Edit/Options.php | 2 +- .../Bundle/Product/Edit/OptionsImportGrid.php | 2 +- .../Adminhtml/Bundle/Product/Edit/Related.php | 2 +- .../Bundle/Product/Edit/RelatedGrid.php | 2 +- .../Adminhtml/Bundle/Product/Edit/Save.php | 2 +- .../Bundle/Product/Edit/ShowUpdateResult.php | 2 +- .../Bundle/Product/Edit/SuggestAttributes.php | 2 +- .../Adminhtml/Bundle/Product/Edit/Upsell.php | 2 +- .../Bundle/Product/Edit/UpsellGrid.php | 2 +- .../Bundle/Product/Edit/Validate.php | 2 +- .../Adminhtml/Bundle/Product/Edit/Wysiwyg.php | 2 +- .../Adminhtml/Bundle/Selection/Grid.php | 2 +- .../Adminhtml/Bundle/Selection/Search.php | 2 +- .../Initialization/Helper/Plugin/Bundle.php | 2 +- .../Helper/Catalog/Product/Configuration.php | 22 +- app/code/Magento/Bundle/Helper/Data.php | 2 +- .../Magento/Bundle/Model/BundleOption.php | 2 +- .../Bundle/Model/CartItemProcessor.php | 2 +- app/code/Magento/Bundle/Model/Link.php | 2 +- .../Magento/Bundle/Model/LinkManagement.php | 2 +- app/code/Magento/Bundle/Model/Option.php | 2 +- .../Magento/Bundle/Model/Option/Validator.php | 2 +- .../Magento/Bundle/Model/OptionManagement.php | 2 +- .../Magento/Bundle/Model/OptionRepository.php | 2 +- .../Magento/Bundle/Model/OptionTypeList.php | 2 +- .../Bundle/Model/Plugin/PriceBackend.php | 2 +- .../Magento/Bundle/Model/Plugin/Product.php | 2 +- .../Magento/Bundle/Model/Plugin/QuoteItem.php | 2 +- .../Product/Attribute/Source/Price/View.php | 2 +- .../Attribute/Source/Shipment/Type.php | 2 +- .../Bundle/Model/Product/CatalogPrice.php | 2 +- .../Model/Product/CopyConstructor/Bundle.php | 2 +- .../Bundle/Model/Product/LinksList.php | 2 +- .../Bundle/Model/Product/OptionList.php | 2 +- .../Magento/Bundle/Model/Product/Price.php | 26 +- .../Bundle/Model/Product/ReadHandler.php | 2 +- .../Bundle/Model/Product/SaveHandler.php | 2 +- .../Magento/Bundle/Model/Product/Type.php | 144 +- .../Bundle/Model/ProductOptionProcessor.php | 2 +- .../Bundle/Model/ResourceModel/Bundle.php | 2 +- .../Model/ResourceModel/Indexer/Price.php | 10 +- .../Model/ResourceModel/Indexer/Stock.php | 2 +- .../Bundle/Model/ResourceModel/Option.php | 2 +- .../Model/ResourceModel/Option/Collection.php | 2 +- .../Bundle/Model/ResourceModel/Selection.php | 2 +- .../ResourceModel/Selection/Collection.php | 128 +- .../Selection/Plugin/Collection.php | 2 +- .../Sales/Order/Pdf/Items/AbstractItems.php | 54 +- .../Sales/Order/Pdf/Items/Creditmemo.php | 17 +- .../Model/Sales/Order/Pdf/Items/Invoice.php | 22 +- .../Model/Sales/Order/Pdf/Items/Shipment.php | 17 +- app/code/Magento/Bundle/Model/Selection.php | 2 +- .../Source/Option/Selection/Price/Type.php | 2 +- .../Bundle/Model/Source/Option/Type.php | 2 +- .../Observer/AppendUpsellProductsObserver.php | 2 +- .../Observer/InitOptionRendererObserver.php | 2 +- .../Observer/LoadProductOptionsObserver.php | 2 +- .../Observer/SetAttributeTabBlockObserver.php | 2 +- .../Adjustment/BundleCalculatorInterface.php | 2 +- .../Bundle/Pricing/Adjustment/Calculator.php | 88 +- .../DefaultSelectionPriceListProvider.php | 208 + .../SelectionPriceListProviderInterface.php | 23 + .../Pricing/Price/BundleOptionPrice.php | 21 +- .../Price/BundleOptionPriceInterface.php | 2 +- .../Pricing/Price/BundleRegularPrice.php | 2 +- .../Pricing/Price/BundleSelectionFactory.php | 12 +- .../Pricing/Price/BundleSelectionPrice.php | 24 +- .../Bundle/Pricing/Price/ConfiguredPrice.php | 22 +- .../Pricing/Price/DiscountCalculator.php | 2 +- .../Price/DiscountProviderInterface.php | 2 +- .../Bundle/Pricing/Price/FinalPrice.php | 2 +- .../Pricing/Price/FinalPriceInterface.php | 2 +- .../Pricing/Price/RegularPriceInterface.php | 2 +- .../Bundle/Pricing/Price/SpecialPrice.php | 2 +- .../Bundle/Pricing/Price/TierPrice.php | 33 +- .../Bundle/Pricing/Render/FinalPriceBox.php | 11 +- app/code/Magento/Bundle/Setup/InstallData.php | 2 +- .../Magento/Bundle/Setup/InstallSchema.php | 8 +- app/code/Magento/Bundle/Setup/Recurring.php | 2 +- app/code/Magento/Bundle/Setup/UpgradeData.php | 2 +- .../Magento/Bundle/Setup/UpgradeSchema.php | 20 +- .../Fieldset/Options/Type/CheckboxTest.php | 2 +- .../Fieldset/Options/Type/MultiTest.php | 2 +- .../Fieldset/Options/Type/RadioTest.php | 2 +- .../Fieldset/Options/Type/SelectTest.php | 2 +- .../Edit/Tab/Attributes/ExtendTest.php | 2 +- .../Product/Edit/Tab/Bundle/OptionTest.php | 2 +- .../Sales/Order/Items/RendererTest.php | 38 +- .../Sales/Order/View/Items/RendererTest.php | 34 +- .../Product/View/Type/Bundle/OptionTest.php | 2 +- .../Catalog/Product/View/Type/BundleTest.php | 349 +- .../Block/Sales/Order/Items/RendererTest.php | 37 +- .../Bundle/Product/Edit/FormTest.php | 2 +- .../Adminhtml/Bundle/Selection/GridTest.php | 2 +- .../Adminhtml/Bundle/Selection/SearchTest.php | 2 +- .../Helper/Plugin/BundleTest.php | 2 +- .../Catalog/Product/ConfigurationTest.php | 33 +- .../Bundle/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Model/CartItemProcessorTest.php | 2 +- .../Test/Unit/Model/LinkManagementTest.php | 2 +- .../Test/Unit/Model/Option/ValidatorTest.php | 2 +- .../Test/Unit/Model/OptionManagementTest.php | 2 +- .../Test/Unit/Model/OptionRepositoryTest.php | 2 +- .../Bundle/Test/Unit/Model/OptionTest.php | 2 +- .../Test/Unit/Model/OptionTypeListTest.php | 2 +- .../Unit/Model/Plugin/PriceBackendTest.php | 2 +- .../Test/Unit/Model/Plugin/ProductTest.php | 2 +- .../Test/Unit/Model/Plugin/QuoteItemTest.php | 2 +- .../Attribute/Source/Price/ViewTest.php | 2 +- .../Unit/Model/Product/CatalogPriceTest.php | 2 +- .../Product/CopyConstructor/BundleTest.php | 2 +- .../Test/Unit/Model/Product/LinksListTest.php | 2 +- .../Unit/Model/Product/OptionListTest.php | 2 +- .../Test/Unit/Model/Product/PriceTest.php | 90 +- .../Test/Unit/Model/Product/TypeTest.php | 480 +- .../Unit/Model/ProductOptionProcessorTest.php | 2 +- .../Order/Pdf/Items/AbstractItemsTest.php | 39 +- .../Pricing/Adjustment/CalculatorTest.php | 72 +- .../Pricing/Price/BundleOptionPriceTest.php | 2 +- .../Pricing/Price/BundleRegularPriceTest.php | 2 +- .../Price/BundleSelectionFactoryTest.php | 24 +- .../Price/BundleSelectionPriceTest.php | 2 +- .../Pricing/Price/DiscountCalculatorTest.php | 2 +- .../Unit/Pricing/Price/FinalPriceTest.php | 2 +- .../Unit/Pricing/Price/SpecialPriceTest.php | 2 +- .../Test/Unit/Pricing/Price/TierPriceTest.php | 16 +- .../Unit/Pricing/Render/FinalPriceBoxTest.php | 17 +- .../Product/BundleDataProviderTest.php | 2 +- .../Form/Modifier/AbstractModifierTest.php | 2 +- .../Form/Modifier/BundleQuantityTest.php | 2 +- .../Product/Form/Modifier/BundleSkuTest.php | 2 +- .../Form/Modifier/BundleWeightTest.php | 2 +- .../Product/Form/Modifier/CompositeTest.php | 2 +- .../Product/BundleDataProvider.php | 2 +- .../Form/Modifier/BundleAdvancedPricing.php | 2 +- .../Form/Modifier/BundleCustomOptions.php | 2 +- .../Product/Form/Modifier/BundlePanel.php | 65 +- .../Product/Form/Modifier/BundlePrice.php | 2 +- .../Product/Form/Modifier/BundleQuantity.php | 2 +- .../Product/Form/Modifier/BundleSku.php | 2 +- .../Product/Form/Modifier/BundleWeight.php | 2 +- .../Product/Form/Modifier/Composite.php | 2 +- .../Product/Form/Modifier/StockData.php | 2 +- app/code/Magento/Bundle/composer.json | 2 +- app/code/Magento/Bundle/etc/adminhtml/di.xml | 6 +- .../Magento/Bundle/etc/adminhtml/events.xml | 2 +- .../Magento/Bundle/etc/adminhtml/routes.xml | 2 +- .../Magento/Bundle/etc/catalog_attributes.xml | 2 +- app/code/Magento/Bundle/etc/config.xml | 2 +- app/code/Magento/Bundle/etc/di.xml | 10 +- .../Bundle/etc/extension_attributes.xml | 2 +- app/code/Magento/Bundle/etc/frontend/di.xml | 2 +- .../Magento/Bundle/etc/frontend/events.xml | 2 +- app/code/Magento/Bundle/etc/module.xml | 4 +- app/code/Magento/Bundle/etc/pdf.xml | 2 +- app/code/Magento/Bundle/etc/product_types.xml | 2 +- app/code/Magento/Bundle/etc/sales.xml | 2 +- app/code/Magento/Bundle/etc/webapi.xml | 2 +- .../Magento/Bundle/etc/webapi_rest/di.xml | 2 +- .../Magento/Bundle/etc/webapi_soap/di.xml | 2 +- app/code/Magento/Bundle/registration.php | 2 +- .../layout/adminhtml_order_shipment_new.xml | 2 +- .../layout/adminhtml_order_shipment_view.xml | 4 +- .../layout/catalog_product_bundle.xml | 2 +- .../adminhtml/layout/catalog_product_new.xml | 2 +- .../catalog_product_view_type_bundle.xml | 2 +- .../layout/customer_index_wishlist.xml | 2 +- .../layout/sales_order_creditmemo_new.xml | 2 +- .../sales_order_creditmemo_updateqty.xml | 2 +- .../layout/sales_order_creditmemo_view.xml | 2 +- .../layout/sales_order_invoice_new.xml | 2 +- .../layout/sales_order_invoice_updateqty.xml | 2 +- .../layout/sales_order_invoice_view.xml | 2 +- .../adminhtml/layout/sales_order_view.xml | 2 +- .../product/edit/tab/attributes/extend.phtml | 2 +- .../composite/fieldset/options/bundle.phtml | 4 +- .../fieldset/options/type/checkbox.phtml | 2 +- .../fieldset/options/type/multi.phtml | 2 +- .../fieldset/options/type/radio.phtml | 2 +- .../fieldset/options/type/select.phtml | 2 +- .../templates/product/edit/bundle.phtml | 2 +- .../product/edit/bundle/option.phtml | 2 +- .../product/edit/bundle/option/search.phtml | 2 +- .../edit/bundle/option/selection.phtml | 2 +- .../templates/product/stock/disabler.phtml | 2 +- .../creditmemo/create/items/renderer.phtml | 2 +- .../creditmemo/view/items/renderer.phtml | 2 +- .../sales/invoice/create/items/renderer.phtml | 2 +- .../sales/invoice/view/items/renderer.phtml | 2 +- .../sales/order/view/items/renderer.phtml | 2 +- .../shipment/create/items/renderer.phtml | 2 +- .../sales/shipment/view/items/renderer.phtml | 2 +- .../ui_component/bundle_product_listing.xml | 2 +- .../view/adminhtml/web/css/bundle-product.css | 2 +- .../view/adminhtml/web/js/bundle-product.js | 274 +- .../adminhtml/web/js/bundle-type-handler.js | 2 +- .../web/js/components/bundle-checkbox.js | 2 +- .../js/components/bundle-dynamic-rows-grid.js | 59 + .../web/js/components/bundle-dynamic-rows.js | 98 + .../web/js/components/bundle-input-type.js | 2 +- .../web/js/components/bundle-option-qty.js | 2 +- .../base/layout/catalog_product_prices.xml | 2 +- .../templates/product/price/final_price.phtml | 2 +- .../product/price/selection/amount.phtml | 2 +- .../templates/product/price/tier_prices.phtml | 4 +- .../Bundle/view/base/web/js/price-bundle.js | 55 +- .../catalog_product_view_type_bundle.xml | 2 +- .../catalog_product_view_type_simple.xml | 2 +- .../checkout_cart_configure_type_bundle.xml | 2 +- .../layout/checkout_cart_item_renderers.xml | 2 +- ...checkout_onepage_review_item_renderers.xml | 2 +- .../Bundle/view/frontend/layout/default.xml | 2 +- ...sales_email_order_creditmemo_renderers.xml | 2 +- .../sales_email_order_invoice_renderers.xml | 2 +- .../layout/sales_email_order_renderers.xml | 2 +- .../sales_email_order_shipment_renderers.xml | 2 +- .../sales_order_creditmemo_renderers.xml | 2 +- .../layout/sales_order_invoice_renderers.xml | 2 +- .../layout/sales_order_item_renderers.xml | 2 +- ...sales_order_print_creditmemo_renderers.xml | 2 +- .../sales_order_print_invoice_renderers.xml | 2 +- .../layout/sales_order_print_renderers.xml | 2 +- .../sales_order_print_shipment_renderers.xml | 2 +- .../layout/sales_order_shipment_renderers.xml | 2 +- .../Bundle/view/frontend/requirejs-config.js | 4 +- .../catalog/product/view/backbutton.phtml | 2 +- .../catalog/product/view/customize.phtml | 2 +- .../catalog/product/view/options/notice.phtml | 2 +- .../catalog/product/view/summary.phtml | 2 +- .../catalog/product/view/type/bundle.phtml | 2 +- .../view/type/bundle/option/checkbox.phtml | 2 +- .../view/type/bundle/option/multi.phtml | 2 +- .../view/type/bundle/option/radio.phtml | 2 +- .../view/type/bundle/option/select.phtml | 2 +- .../product/view/type/bundle/options.phtml | 2 +- .../order/items/creditmemo/default.phtml | 2 +- .../email/order/items/invoice/default.phtml | 2 +- .../email/order/items/order/default.phtml | 2 +- .../email/order/items/shipment/default.phtml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../order/creditmemo/items/renderer.phtml | 2 +- .../sales/order/invoice/items/renderer.phtml | 2 +- .../sales/order/items/renderer.phtml | 2 +- .../sales/order/shipment/items/renderer.phtml | 2 +- .../Bundle/view/frontend/web/js/float.js | 29 +- .../view/frontend/web/js/product-summary.js | 5 +- .../Bundle/view/frontend/web/js/slide.js | 55 +- .../Model/Export/Product/Type/Bundle.php | 2 +- .../Model/Export/RowCustomizer.php | 46 +- .../Model/Import/Product/Type/Bundle.php | 34 +- .../Export/Product/RowCustomizerTest.php | 7 +- .../Model/Import/Product/Type/BundleTest.php | 2 +- .../Magento/BundleImportExport/composer.json | 3 +- .../Magento/BundleImportExport/etc/di.xml | 2 +- .../Magento/BundleImportExport/etc/export.xml | 2 +- .../Magento/BundleImportExport/etc/import.xml | 2 +- .../Magento/BundleImportExport/etc/module.xml | 2 +- .../BundleImportExport/registration.php | 2 +- .../CacheInvalidate/Model/PurgeCache.php | 2 +- .../CacheInvalidate/Model/SocketFactory.php | 2 +- .../Observer/FlushAllCacheObserver.php | 2 +- .../Observer/InvalidateVarnishObserver.php | 47 +- .../Test/Unit/Model/PurgeCacheTest.php | 2 +- .../Test/Unit/Model/SocketFactoryTest.php | 2 +- .../Observer/FlushAllCacheObserverTest.php | 2 +- .../InvalidateVarnishObserverTest.php | 17 +- .../Magento/CacheInvalidate/composer.json | 2 +- .../Magento/CacheInvalidate/etc/events.xml | 4 +- .../Magento/CacheInvalidate/etc/module.xml | 2 +- .../Magento/CacheInvalidate/registration.php | 2 +- .../Adminhtml/Captcha/DefaultCaptcha.php | 2 +- app/code/Magento/Captcha/Block/Captcha.php | 2 +- .../Captcha/Block/Captcha/DefaultCaptcha.php | 2 +- .../Controller/Adminhtml/Refresh/Refresh.php | 32 +- .../Captcha/Controller/Refresh/Index.php | 34 +- .../Captcha/Cron/DeleteExpiredImages.php | 2 +- .../Captcha/Cron/DeleteOldAttempts.php | 2 +- .../Magento/Captcha/Helper/Adminhtml/Data.php | 2 +- app/code/Magento/Captcha/Helper/Data.php | 2 +- .../Magento/Captcha/Model/CaptchaFactory.php | 2 +- .../Captcha/Model/CaptchaInterface.php | 2 +- .../Captcha/Model/Cart/ConfigPlugin.php | 2 +- .../Captcha/Model/Checkout/ConfigProvider.php | 2 +- .../Magento/Captcha/Model/Config/Font.php | 2 +- .../Model/Config/Form/AbstractForm.php | 2 +- .../Captcha/Model/Config/Form/Backend.php | 2 +- .../Captcha/Model/Config/Form/Frontend.php | 2 +- .../Magento/Captcha/Model/Config/Mode.php | 2 +- .../Model/Customer/Plugin/AjaxLogin.php | 17 +- .../Magento/Captcha/Model/DefaultModel.php | 186 +- .../Captcha/Model/ResourceModel/Log.php | 11 +- .../Observer/CaptchaStringResolver.php | 2 +- .../Observer/CheckContactUsFormObserver.php | 2 +- .../Observer/CheckForgotpasswordObserver.php | 2 +- .../Observer/CheckGuestCheckoutObserver.php | 2 +- .../CheckRegisterCheckoutObserver.php | 2 +- .../Observer/CheckUserCreateObserver.php | 2 +- .../Observer/CheckUserEditObserver.php | 2 +- ...CheckUserForgotPasswordBackendObserver.php | 2 +- .../CheckUserLoginBackendObserver.php | 2 +- .../Observer/CheckUserLoginObserver.php | 2 +- .../ResetAttemptForBackendObserver.php | 2 +- ...tAttemptForFrontendAccountEditObserver.php | 2 +- .../ResetAttemptForFrontendObserver.php | 2 +- .../Magento/Captcha/Setup/InstallSchema.php | 2 +- .../Unit/Controller/Refresh/IndexTest.php | 27 +- .../Unit/Cron/DeleteExpiredImagesTest.php | 2 +- .../Test/Unit/Helper/Adminhtml/DataTest.php | 2 +- .../Captcha/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Model/CaptchaFactoryTest.php | 2 +- .../Test/Unit/Model/Cart/ConfigPluginTest.php | 2 +- .../Model/Checkout/ConfigProviderTest.php | 2 +- .../Model/Customer/Plugin/AjaxLoginTest.php | 38 +- .../Captcha/Test/Unit/Model/DefaultTest.php | 2 +- .../CheckContactUsFormObserverTest.php | 2 +- .../CheckForgotpasswordObserverTest.php | 2 +- .../Observer/CheckUserCreateObserverTest.php | 2 +- .../Observer/CheckUserEditObserverTest.php | 2 +- .../Observer/CheckUserLoginObserverTest.php | 2 +- app/code/Magento/Captcha/composer.json | 7 +- app/code/Magento/Captcha/etc/adminhtml/di.xml | 2 +- .../Magento/Captcha/etc/adminhtml/events.xml | 2 +- .../Magento/Captcha/etc/adminhtml/routes.xml | 2 +- .../Magento/Captcha/etc/adminhtml/system.xml | 2 +- app/code/Magento/Captcha/etc/config.xml | 2 +- app/code/Magento/Captcha/etc/crontab.xml | 2 +- app/code/Magento/Captcha/etc/crontab/di.xml | 2 +- app/code/Magento/Captcha/etc/di.xml | 2 +- app/code/Magento/Captcha/etc/events.xml | 2 +- app/code/Magento/Captcha/etc/frontend/di.xml | 2 +- .../Magento/Captcha/etc/frontend/events.xml | 2 +- .../Magento/Captcha/etc/frontend/routes.xml | 4 +- app/code/Magento/Captcha/etc/module.xml | 2 +- app/code/Magento/Captcha/registration.php | 2 +- .../layout/adminhtml_auth_forgotpassword.xml | 2 +- .../adminhtml/layout/adminhtml_auth_login.xml | 2 +- .../view/adminhtml/templates/default.phtml | 2 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../frontend/layout/contact_index_index.xml | 2 +- .../layout/customer_account_create.xml | 2 +- .../frontend/layout/customer_account_edit.xml | 2 +- .../customer_account_forgotpassword.xml | 2 +- .../layout/customer_account_login.xml | 2 +- .../Captcha/view/frontend/layout/default.xml | 2 +- .../Captcha/view/frontend/requirejs-config.js | 4 +- .../view/frontend/templates/default.phtml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../Captcha/view/frontend/web/captcha.js | 29 +- .../view/frontend/web/js/action/refresh.js | 43 +- .../view/frontend/web/js/model/captcha.js | 233 +- .../view/frontend/web/js/model/captchaList.js | 32 +- .../web/js/view/checkout/defaultCaptcha.js | 185 +- .../web/js/view/checkout/loginCaptcha.js | 59 +- .../Captcha/view/frontend/web/onepage.js | 27 +- .../web/template/checkout/captcha.html | 2 +- .../Api/AttributeSetFinderInterface.php | 2 +- .../Api/AttributeSetManagementInterface.php | 2 +- .../Api/AttributeSetRepositoryInterface.php | 2 +- .../Catalog/Api/BasePriceStorageInterface.php | 36 + ...goryAttributeOptionManagementInterface.php | 2 +- .../CategoryAttributeRepositoryInterface.php | 2 +- .../Api/CategoryLinkManagementInterface.php | 2 +- .../Api/CategoryLinkRepositoryInterface.php | 2 +- .../Catalog/Api/CategoryListInterface.php | 2 +- .../Api/CategoryManagementInterface.php | 2 +- .../Api/CategoryRepositoryInterface.php | 2 +- .../Catalog/Api/CostStorageInterface.php | 47 + .../Catalog/Api/Data/BasePriceInterface.php | 84 + .../Api/Data/CategoryAttributeInterface.php | 2 +- ...ategoryAttributeSearchResultsInterface.php | 2 +- .../Catalog/Api/Data/CategoryInterface.php | 2 +- .../Api/Data/CategoryLinkInterface.php | 2 +- .../Api/Data/CategoryProductLinkInterface.php | 2 +- .../CategoryProductSearchResultInterface.php | 2 +- .../Data/CategorySearchResultsInterface.php | 2 +- .../Api/Data/CategoryTreeInterface.php | 2 +- .../Catalog/Api/Data/CostInterface.php | 84 + .../Api/Data/CustomOptionInterface.php | 2 +- .../Api/Data/EavAttributeInterface.php | 2 +- .../Api/Data/PriceUpdateResultInterface.php | 69 + .../Api/Data/ProductAttributeInterface.php | 2 +- ...uctAttributeMediaGalleryEntryInterface.php | 2 +- ...ProductAttributeSearchResultsInterface.php | 2 +- .../Data/ProductAttributeTypeInterface.php | 2 +- .../Api/Data/ProductCustomOptionInterface.php | 2 +- .../Data/ProductCustomOptionTypeInterface.php | 2 +- .../ProductCustomOptionValuesInterface.php | 2 +- .../Catalog/Api/Data/ProductInterface.php | 2 +- .../Data/ProductLinkAttributeInterface.php | 2 +- .../Catalog/Api/Data/ProductLinkInterface.php | 2 +- .../Api/Data/ProductLinkTypeInterface.php | 2 +- .../Api/Data/ProductOptionInterface.php | 2 +- .../Data/ProductSearchResultsInterface.php | 2 +- .../Api/Data/ProductTierPriceInterface.php | 2 +- .../Catalog/Api/Data/ProductTypeInterface.php | 2 +- .../Api/Data/ProductWebsiteLinkInterface.php | 2 +- .../Api/Data/SpecialPriceInterface.php | 117 + .../Catalog/Api/Data/TierPriceInterface.php | 134 + ...oductAttributeGroupRepositoryInterface.php | 2 +- .../ProductAttributeManagementInterface.php | 2 +- ...tributeMediaGalleryManagementInterface.php | 2 +- ...ductAttributeOptionManagementInterface.php | 2 +- .../ProductAttributeRepositoryInterface.php | 2 +- .../ProductAttributeTypesListInterface.php | 2 +- ...ProductCustomOptionRepositoryInterface.php | 2 +- .../ProductCustomOptionTypeListInterface.php | 2 +- .../Api/ProductLinkManagementInterface.php | 2 +- .../Api/ProductLinkRepositoryInterface.php | 2 +- .../Api/ProductLinkTypeListInterface.php | 2 +- .../Api/ProductManagementInterface.php | 2 +- ...oductMediaAttributeManagementInterface.php | 2 +- .../Api/ProductRepositoryInterface.php | 2 +- .../ProductTierPriceManagementInterface.php | 2 +- .../Catalog/Api/ProductTypeListInterface.php | 2 +- .../ProductWebsiteLinkRepositoryInterface.php | 2 +- ...pedProductTierPriceManagementInterface.php | 2 +- .../Catalog/Api/SpecialPriceInterface.php | 67 + .../Api/SpecialPriceStorageInterface.php | 49 + .../Catalog/Api/TierPriceStorageInterface.php | 62 + .../Adminhtml/Category/AbstractCategory.php | 2 +- .../Adminhtml/Category/AssignProducts.php | 2 +- .../Adminhtml/Category/Checkboxes/Tree.php | 2 +- .../Catalog/Block/Adminhtml/Category/Edit.php | 2 +- .../Adminhtml/Category/Edit/DeleteButton.php | 2 +- .../Adminhtml/Category/Edit/SaveButton.php | 2 +- .../Block/Adminhtml/Category/Helper/Image.php | 2 +- .../Adminhtml/Category/Helper/Pricestep.php | 2 +- .../Category/Helper/Sortby/Available.php | 2 +- .../Category/Helper/Sortby/DefaultSortby.php | 2 +- .../Block/Adminhtml/Category/Tab/Product.php | 2 +- .../Catalog/Block/Adminhtml/Category/Tree.php | 2 +- .../Adminhtml/Category/Widget/Chooser.php | 2 +- .../Magento/Catalog/Block/Adminhtml/Form.php | 2 +- .../Form/Renderer/Config/DateFieldsOrder.php | 2 +- .../Form/Renderer/Config/YearRange.php | 2 +- .../Form/Renderer/Fieldset/Element.php | 2 +- .../Block/Adminhtml/Helper/Form/Wysiwyg.php | 2 +- .../Adminhtml/Helper/Form/Wysiwyg/Content.php | 8 +- .../Catalog/Block/Adminhtml/Product.php | 2 +- .../Block/Adminhtml/Product/Attribute.php | 2 +- .../Product/Attribute/Button/Cancel.php | 2 +- .../Product/Attribute/Button/Generic.php | 2 +- .../Product/Attribute/Button/Save.php | 2 +- .../Button/SaveInNewAttributeSet.php | 2 +- .../Adminhtml/Product/Attribute/Edit.php | 2 +- .../Adminhtml/Product/Attribute/Edit/Form.php | 2 +- .../Product/Attribute/Edit/Tab/Advanced.php | 2 +- .../Product/Attribute/Edit/Tab/Front.php | 2 +- .../Product/Attribute/Edit/Tab/Main.php | 2 +- .../Product/Attribute/Edit/Tab/Options.php | 2 +- .../Product/Attribute/Edit/Tab/System.php | 2 +- .../Adminhtml/Product/Attribute/Edit/Tabs.php | 2 +- .../Adminhtml/Product/Attribute/Grid.php | 2 +- .../NewAttribute/Product/Attributes.php | 2 +- .../Adminhtml/Product/Attribute/Set/Main.php | 2 +- .../Attribute/Set/Main/Formattribute.php | 2 +- .../Product/Attribute/Set/Main/Formgroup.php | 2 +- .../Product/Attribute/Set/Main/Formset.php | 2 +- .../Attribute/Set/Main/Tree/Attribute.php | 2 +- .../Product/Attribute/Set/Main/Tree/Group.php | 2 +- .../Product/Attribute/Set/Toolbar/Add.php | 2 +- .../Product/Attribute/Set/Toolbar/Main.php | 2 +- .../Attribute/Set/Toolbar/Main/Filter.php | 2 +- .../Adminhtml/Product/Composite/Configure.php | 2 +- .../Adminhtml/Product/Composite/Error.php | 2 +- .../Adminhtml/Product/Composite/Fieldset.php | 2 +- .../Product/Composite/Fieldset/Options.php | 2 +- .../Product/Composite/Fieldset/Qty.php | 2 +- .../Product/Composite/Update/Result.php | 2 +- .../Catalog/Block/Adminhtml/Product/Edit.php | 2 +- .../Product/Edit/Action/Attribute.php | 2 +- .../Edit/Action/Attribute/Tab/Attributes.php | 2 +- .../Edit/Action/Attribute/Tab/Inventory.php | 2 +- .../Edit/Action/Attribute/Tab/Websites.php | 2 +- .../Product/Edit/Action/Attribute/Tabs.php | 2 +- .../Adminhtml/Product/Edit/AttributeSet.php | 2 +- .../Product/Edit/Button/AddAttribute.php | 2 +- .../Adminhtml/Product/Edit/Button/Back.php | 2 +- .../Product/Edit/Button/CreateCategory.php | 2 +- .../Adminhtml/Product/Edit/Button/Generic.php | 2 +- .../Adminhtml/Product/Edit/Button/Save.php | 2 +- .../Block/Adminhtml/Product/Edit/Js.php | 2 +- .../Adminhtml/Product/Edit/NewCategory.php | 2 +- .../Product/Edit/Tab/Ajax/Serializer.php | 33 +- .../Adminhtml/Product/Edit/Tab/Alerts.php | 2 +- .../Product/Edit/Tab/Alerts/Price.php | 2 +- .../Product/Edit/Tab/Alerts/Stock.php | 2 +- .../Adminhtml/Product/Edit/Tab/Attributes.php | 2 +- .../Product/Edit/Tab/Attributes/Create.php | 2 +- .../Product/Edit/Tab/Attributes/Search.php | 2 +- .../Adminhtml/Product/Edit/Tab/ChildTab.php | 2 +- .../Adminhtml/Product/Edit/Tab/Crosssell.php | 2 +- .../Adminhtml/Product/Edit/Tab/Inventory.php | 2 +- .../Adminhtml/Product/Edit/Tab/Options.php | 2 +- .../Product/Edit/Tab/Options/Option.php | 2 +- .../Product/Edit/Tab/Options/Popup/Grid.php | 2 +- .../Edit/Tab/Options/Type/AbstractType.php | 2 +- .../Product/Edit/Tab/Options/Type/Date.php | 2 +- .../Product/Edit/Tab/Options/Type/File.php | 2 +- .../Product/Edit/Tab/Options/Type/Select.php | 2 +- .../Product/Edit/Tab/Options/Type/Text.php | 2 +- .../Adminhtml/Product/Edit/Tab/Price.php | 2 +- .../Edit/Tab/Price/Group/AbstractGroup.php | 2 +- .../Adminhtml/Product/Edit/Tab/Price/Tier.php | 2 +- .../Adminhtml/Product/Edit/Tab/Related.php | 2 +- .../Adminhtml/Product/Edit/Tab/Upsell.php | 2 +- .../Adminhtml/Product/Edit/Tab/Websites.php | 2 +- .../Block/Adminhtml/Product/Edit/Tabs.php | 2 +- .../Product/Frontend/Product/Watermark.php | 2 +- .../Catalog/Block/Adminhtml/Product/Grid.php | 2 +- .../Adminhtml/Product/Helper/Form/Apply.php | 2 +- .../Adminhtml/Product/Helper/Form/Boolean.php | 2 +- .../Product/Helper/Form/Category.php | 2 +- .../Adminhtml/Product/Helper/Form/Config.php | 2 +- .../Adminhtml/Product/Helper/Form/Gallery.php | 2 +- .../Product/Helper/Form/Gallery/Content.php | 2 +- .../Adminhtml/Product/Helper/Form/Image.php | 2 +- .../Adminhtml/Product/Helper/Form/Price.php | 2 +- .../Adminhtml/Product/Helper/Form/Weight.php | 2 +- .../Block/Adminhtml/Product/Options/Ajax.php | 2 +- .../Catalog/Block/Adminhtml/Product/Price.php | 2 +- .../Adminhtml/Product/Widget/Chooser.php | 2 +- .../Product/Widget/Chooser/Container.php | 2 +- .../Catalog/Block/Adminhtml/Rss/Grid/Link.php | 2 +- .../Block/Adminhtml/Rss/NotifyStock.php | 2 +- .../Magento/Catalog/Block/Breadcrumbs.php | 2 +- .../Block/Category/Plugin/PriceBoxTags.php | 2 +- .../Catalog/Block/Category/Rss/Link.php | 2 +- .../Magento/Catalog/Block/Category/View.php | 2 +- app/code/Magento/Catalog/Block/Navigation.php | 2 +- .../Catalog/Block/Product/AbstractProduct.php | 4 +- .../Catalog/Block/Product/AwareInterface.php | 2 +- .../Block/Product/Compare/ListCompare.php | 2 +- .../Magento/Catalog/Block/Product/Context.php | 2 +- .../Magento/Catalog/Block/Product/Gallery.php | 2 +- .../Magento/Catalog/Block/Product/Image.php | 2 +- .../Catalog/Block/Product/ImageBuilder.php | 2 +- .../Catalog/Block/Product/ListProduct.php | 196 +- .../Catalog/Block/Product/NewProduct.php | 2 +- .../Magento/Catalog/Block/Product/Price.php | 2 +- .../Block/Product/ProductList/Crosssell.php | 2 +- .../ProductList/Item/AddTo/Compare.php | 2 +- .../Block/Product/ProductList/Item/Block.php | 2 +- .../Product/ProductList/Item/Container.php | 2 +- .../Block/Product/ProductList/Promotion.php | 2 +- .../Block/Product/ProductList/Random.php | 2 +- .../Block/Product/ProductList/Related.php | 2 +- .../Block/Product/ProductList/Toolbar.php | 2 +- .../Block/Product/ProductList/Upsell.php | 2 +- .../ReviewRenderer/DefaultProvider.php | 2 +- .../Block/Product/ReviewRendererInterface.php | 2 +- .../Block/Product/TemplateSelector.php | 2 +- .../Magento/Catalog/Block/Product/View.php | 32 +- .../Block/Product/View/AbstractView.php | 2 +- .../Block/Product/View/AddTo/Compare.php | 2 +- .../Catalog/Block/Product/View/Additional.php | 2 +- .../Catalog/Block/Product/View/Attributes.php | 2 +- .../Catalog/Block/Product/View/BaseImage.php | 2 +- .../Block/Product/View/Description.php | 2 +- .../Catalog/Block/Product/View/Gallery.php | 8 +- .../Catalog/Block/Product/View/Options.php | 2 +- .../Product/View/Options/AbstractOptions.php | 2 +- .../Block/Product/View/Options/Type/Date.php | 2 +- .../Product/View/Options/Type/DefaultType.php | 2 +- .../Block/Product/View/Options/Type/File.php | 2 +- .../Product/View/Options/Type/Select.php | 2 +- .../Block/Product/View/Options/Type/Text.php | 2 +- .../Catalog/Block/Product/View/Price.php | 2 +- .../Catalog/Block/Product/View/Tabs.php | 2 +- .../Block/Product/View/Type/Simple.php | 2 +- .../Block/Product/View/Type/Virtual.php | 2 +- .../Block/Product/Widget/Html/Pager.php | 2 +- .../Block/Product/Widget/NewWidget.php | 2 +- .../Magento/Catalog/Block/Rss/Category.php | 2 +- .../Catalog/Block/Rss/Product/NewProducts.php | 2 +- .../Catalog/Block/Rss/Product/Special.php | 2 +- .../Magento/Catalog/Block/ShortcutButtons.php | 2 +- .../Catalog/Block/ShortcutInterface.php | 2 +- .../Magento/Catalog/Block/Widget/Link.php | 2 +- .../Console/Command/ImagesResizeCommand.php | 2 +- .../Command/ProductAttributesCleanUp.php | 2 +- .../Catalog/Controller/Adminhtml/Category.php | 16 +- .../Controller/Adminhtml/Category/Add.php | 2 +- .../Adminhtml/Category/CategoriesJson.php | 2 +- .../Controller/Adminhtml/Category/Delete.php | 2 +- .../Controller/Adminhtml/Category/Edit.php | 5 +- .../Controller/Adminhtml/Category/Grid.php | 2 +- .../Adminhtml/Category/Image/Upload.php | 6 +- .../Controller/Adminhtml/Category/Index.php | 2 +- .../Controller/Adminhtml/Category/Move.php | 4 +- .../Adminhtml/Category/RefreshPath.php | 2 +- .../Controller/Adminhtml/Category/Save.php | 40 +- .../Adminhtml/Category/SuggestCategories.php | 2 +- .../Controller/Adminhtml/Category/Tree.php | 2 +- .../Adminhtml/Category/Validate.php | 2 +- .../Controller/Adminhtml/Category/Widget.php | 2 +- .../Category/Widget/CategoriesJson.php | 2 +- .../Adminhtml/Category/Widget/Chooser.php | 2 +- .../Controller/Adminhtml/Category/Wysiwyg.php | 2 +- .../Catalog/Controller/Adminhtml/Product.php | 2 +- .../Adminhtml/Product/AbstractProductGrid.php | 2 +- .../Adminhtml/Product/Action/Attribute.php | 2 +- .../Product/Action/Attribute/Edit.php | 2 +- .../Product/Action/Attribute/Save.php | 4 +- .../Product/Action/Attribute/Validate.php | 2 +- .../Product/AddAttributeToTemplate.php | 21 +- .../Adminhtml/Product/AlertsPriceGrid.php | 2 +- .../Adminhtml/Product/AlertsStockGrid.php | 2 +- .../Adminhtml/Product/Attribute.php | 2 +- .../Adminhtml/Product/Attribute/Delete.php | 2 +- .../Adminhtml/Product/Attribute/Edit.php | 2 +- .../Adminhtml/Product/Attribute/Index.php | 2 +- .../Adminhtml/Product/Attribute/NewAction.php | 2 +- .../Adminhtml/Product/Attribute/Save.php | 2 +- .../Adminhtml/Product/Attribute/Validate.php | 2 +- .../Controller/Adminhtml/Product/Builder.php | 2 +- .../Adminhtml/Product/Categories.php | 2 +- .../Adminhtml/Product/Crosssell.php | 2 +- .../Adminhtml/Product/CrosssellGrid.php | 2 +- .../Adminhtml/Product/CustomOptions.php | 2 +- .../Adminhtml/Product/Datafeeds/Index.php | 2 +- .../Adminhtml/Product/Duplicate.php | 2 +- .../Controller/Adminhtml/Product/Edit.php | 2 +- .../Adminhtml/Product/Gallery/Upload.php | 2 +- .../Controller/Adminhtml/Product/Grid.php | 2 +- .../Controller/Adminhtml/Product/GridOnly.php | 2 +- .../Adminhtml/Product/Group/Save.php | 2 +- .../Controller/Adminhtml/Product/Index.php | 2 +- .../Product/Initialization/Helper.php | 45 +- .../Initialization/Helper/HandlerFactory.php | 2 +- .../Helper/HandlerInterface.php | 2 +- .../Helper/Plugin/Handler/Composite.php | 2 +- .../Initialization/StockDataFilter.php | 2 +- .../Adminhtml/Product/MassDelete.php | 2 +- .../Adminhtml/Product/MassStatus.php | 7 +- .../Adminhtml/Product/NewAction.php | 2 +- .../Controller/Adminhtml/Product/Options.php | 2 +- .../Adminhtml/Product/OptionsImportGrid.php | 2 +- .../Controller/Adminhtml/Product/Related.php | 2 +- .../Adminhtml/Product/RelatedGrid.php | 2 +- .../Controller/Adminhtml/Product/Reload.php | 2 +- .../Controller/Adminhtml/Product/Save.php | 2 +- .../Controller/Adminhtml/Product/Set.php | 2 +- .../Controller/Adminhtml/Product/Set/Add.php | 2 +- .../Adminhtml/Product/Set/Delete.php | 2 +- .../Controller/Adminhtml/Product/Set/Edit.php | 2 +- .../Adminhtml/Product/Set/Index.php | 2 +- .../Controller/Adminhtml/Product/Set/Save.php | 5 +- .../Adminhtml/Product/Set/SetGrid.php | 2 +- .../Adminhtml/Product/ShowUpdateResult.php | 2 +- .../Product/SuggestAttributeSets.php | 2 +- .../Adminhtml/Product/SuggestAttributes.php | 2 +- .../Controller/Adminhtml/Product/Upsell.php | 2 +- .../Adminhtml/Product/UpsellGrid.php | 2 +- .../Controller/Adminhtml/Product/Validate.php | 2 +- .../Adminhtml/Product/Widget/Chooser.php | 2 +- .../Controller/Adminhtml/Product/Wysiwyg.php | 2 +- .../Catalog/Controller/Category/View.php | 9 +- .../Catalog/Controller/Index/Index.php | 2 +- .../Magento/Catalog/Controller/Product.php | 2 +- .../Catalog/Controller/Product/Compare.php | 2 +- .../Controller/Product/Compare/Add.php | 2 +- .../Controller/Product/Compare/Clear.php | 2 +- .../Controller/Product/Compare/Index.php | 2 +- .../Controller/Product/Compare/Remove.php | 2 +- .../Catalog/Controller/Product/Gallery.php | 2 +- .../Catalog/Controller/Product/View.php | 2 +- .../Controller/Product/View/ViewInterface.php | 2 +- .../Cron/DeleteAbandonedStoreFlatTables.php | 2 +- .../Cron/DeleteOutdatedPriceValues.php | 74 + .../Catalog/Cron/RefreshSpecialPrices.php | 2 +- .../Catalog/CustomerData/CompareProducts.php | 2 +- app/code/Magento/Catalog/Helper/Catalog.php | 2 +- app/code/Magento/Catalog/Helper/Category.php | 2 +- app/code/Magento/Catalog/Helper/Data.php | 6 +- .../Catalog/Helper/DefaultCategory.php | 2 +- app/code/Magento/Catalog/Helper/Image.php | 16 +- app/code/Magento/Catalog/Helper/Output.php | 2 +- app/code/Magento/Catalog/Helper/Product.php | 2 +- .../Catalog/Helper/Product/Compare.php | 14 +- .../Catalog/Helper/Product/Composite.php | 2 +- .../Catalog/Helper/Product/Configuration.php | 16 +- .../Configuration/ConfigurationInterface.php | 2 +- .../Helper/Product/ConfigurationPool.php | 2 +- .../Helper/Product/Edit/Action/Attribute.php | 2 +- .../Catalog/Helper/Product/Flat/Indexer.php | 2 +- .../Catalog/Helper/Product/ProductList.php | 2 +- .../Magento/Catalog/Helper/Product/View.php | 19 +- .../Magento/Catalog/Model/AbstractModel.php | 2 +- .../FilterProcessor/ProductCategoryFilter.php | 2 +- .../Attribute/Backend/Customlayoutupdate.php | 2 +- .../Model/Attribute/Backend/Startdate.php | 8 +- .../Catalog/Model/Attribute/Config.php | 2 +- .../Model/Attribute/Config/Converter.php | 2 +- .../Catalog/Model/Attribute/Config/Data.php | 19 +- .../Catalog/Model/Attribute/Config/Reader.php | 2 +- .../Model/Attribute/Config/SchemaLocator.php | 2 +- .../Attribute/LockValidatorComposite.php | 2 +- .../Attribute/LockValidatorInterface.php | 2 +- .../Model/Attribute/ScopeOverriddenValue.php | 2 +- .../Catalog/Model/Attribute/Source/Scopes.php | 2 +- app/code/Magento/Catalog/Model/Category.php | 14 +- .../Catalog/Model/Category/Attribute.php | 2 +- .../Category/Attribute/Backend/Image.php | 66 +- .../Category/Attribute/Backend/Sortby.php | 2 +- .../Category/Attribute/OptionManagement.php | 2 +- .../Category/Attribute/Source/Layout.php | 2 +- .../Model/Category/Attribute/Source/Mode.php | 2 +- .../Model/Category/Attribute/Source/Page.php | 2 +- .../Category/Attribute/Source/Sortby.php | 2 +- .../Model/Category/AttributeRepository.php | 2 +- .../Catalog/Model/Category/DataProvider.php | 210 +- .../Catalog/Model/Category/FileInfo.php | 107 + .../Model/Category/Link/ReadHandler.php | 2 +- .../Model/Category/Link/SaveHandler.php | 2 +- .../Magento/Catalog/Model/Category/Tree.php | 2 +- .../Magento/Catalog/Model/CategoryLink.php | 2 +- .../Catalog/Model/CategoryLinkManagement.php | 2 +- .../Catalog/Model/CategoryLinkRepository.php | 2 +- .../Magento/Catalog/Model/CategoryList.php | 2 +- .../Catalog/Model/CategoryManagement.php | 2 +- .../Catalog/Model/CategoryProductLink.php | 2 +- .../Catalog/Model/CategoryRepository.php | 2 +- app/code/Magento/Catalog/Model/Config.php | 16 +- .../Catalog/Model/Config/Backend/Category.php | 2 +- .../Model/Config/CatalogClone/Media/Image.php | 2 +- .../Catalog/Model/Config/Source/Category.php | 2 +- .../Model/Config/Source/GridPerPage.php | 2 +- .../Catalog/Model/Config/Source/ListMode.php | 2 +- .../Model/Config/Source/ListPerPage.php | 2 +- .../Catalog/Model/Config/Source/ListSort.php | 2 +- .../Model/Config/Source/Price/Scope.php | 2 +- .../Model/Config/Source/Price/Step.php | 2 +- .../Config/Source/Product/Options/Price.php | 2 +- .../Source/Product/Options/TierPrice.php | 27 + .../Config/Source/Product/Options/Type.php | 2 +- .../Model/Config/Source/Product/Thumbnail.php | 2 +- .../Source/ProductPriceOptionsInterface.php | 2 +- .../Model/Config/Source/TimeFormat.php | 2 +- .../Config/Source/Watermark/Position.php | 2 +- .../Model/CustomOptions/CustomOption.php | 2 +- .../CustomOptions/CustomOptionProcessor.php | 17 +- app/code/Magento/Catalog/Model/Design.php | 2 +- .../Catalog/Model/Entity/Attribute.php | 2 +- .../Attribute/Design/Options/Container.php | 2 +- .../Attribute/Group/AttributeMapper.php | 2 +- .../Group/AttributeMapperInterface.php | 2 +- .../Magento/Catalog/Model/EntityInterface.php | 2 +- app/code/Magento/Catalog/Model/Factory.php | 2 +- .../Magento/Catalog/Model/ImageExtractor.php | 2 +- .../Magento/Catalog/Model/ImageUploader.php | 2 +- .../Model/Indexer/AbstractFlatState.php | 2 +- .../Catalog/Model/Indexer/Category/Flat.php | 2 +- .../Indexer/Category/Flat/AbstractAction.php | 2 +- .../Indexer/Category/Flat/Action/Full.php | 2 +- .../Indexer/Category/Flat/Action/Rows.php | 2 +- .../Flat/Plugin/IndexerConfigData.php | 2 +- .../Category/Flat/Plugin/StoreGroup.php | 2 +- .../Category/Flat/Plugin/StoreView.php | 2 +- .../Flat/SkipStaticColumnsProvider.php | 2 +- .../Model/Indexer/Category/Flat/State.php | 2 +- .../Category/Flat/System/Config/Mode.php | 2 +- .../Model/Indexer/Category/Product.php | 2 +- .../Category/Product/AbstractAction.php | 44 +- .../Indexer/Category/Product/Action/Full.php | 2 +- .../Indexer/Category/Product/Action/Rows.php | 2 +- .../Category/Product/Action/RowsFactory.php | 2 +- .../Category/Product/Plugin/MviewState.php | 2 +- .../Category/Product/Plugin/StoreGroup.php | 2 +- .../Category/Product/Plugin/StoreView.php | 2 +- .../Indexer/Category/Product/Processor.php | 2 +- .../Model/Indexer/Product/Category.php | 2 +- .../Indexer/Product/Category/Action/Rows.php | 2 +- .../Product/Category/Action/RowsFactory.php | 2 +- .../Indexer/Product/Category/Processor.php | 2 +- .../Catalog/Model/Indexer/Product/Eav.php | 2 +- .../Indexer/Product/Eav/AbstractAction.php | 2 +- .../Model/Indexer/Product/Eav/Action/Full.php | 2 +- .../Model/Indexer/Product/Eav/Action/Row.php | 2 +- .../Model/Indexer/Product/Eav/Action/Rows.php | 2 +- .../Product/Eav/Plugin/AttributeSet.php | 2 +- .../AttributeSet/IndexableAttributeFilter.php | 2 +- .../Indexer/Product/Eav/Plugin/StoreView.php | 2 +- .../Model/Indexer/Product/Eav/Processor.php | 2 +- .../Catalog/Model/Indexer/Product/Flat.php | 2 +- .../Indexer/Product/Flat/AbstractAction.php | 2 +- .../Indexer/Product/Flat/Action/Eraser.php | 2 +- .../Indexer/Product/Flat/Action/Full.php | 2 +- .../Indexer/Product/Flat/Action/Indexer.php | 2 +- .../Model/Indexer/Product/Flat/Action/Row.php | 2 +- .../Indexer/Product/Flat/Action/Rows.php | 2 +- .../Product/Flat/Action/Rows/TableData.php | 2 +- .../Indexer/Product/Flat/FlatTableBuilder.php | 12 +- .../Product/Flat/Plugin/IndexerConfigData.php | 2 +- .../Indexer/Product/Flat/Plugin/Store.php | 2 +- .../Product/Flat/Plugin/StoreGroup.php | 2 +- .../Model/Indexer/Product/Flat/Processor.php | 2 +- .../Model/Indexer/Product/Flat/State.php | 2 +- .../Product/Flat/System/Config/Mode.php | 2 +- .../Indexer/Product/Flat/Table/Builder.php | 45 + .../Product/Flat/Table/BuilderInterface.php | 40 + .../Indexer/Product/Flat/TableBuilder.php | 59 +- .../Model/Indexer/Product/Flat/TableData.php | 2 +- .../Product/Flat/TableDataInterface.php | 2 +- .../Catalog/Model/Indexer/Product/Price.php | 2 +- .../Indexer/Product/Price/AbstractAction.php | 2 +- .../Indexer/Product/Price/Action/Full.php | 2 +- .../Indexer/Product/Price/Action/Row.php | 2 +- .../Indexer/Product/Price/Action/Rows.php | 2 +- .../Product/Price/Plugin/AbstractPlugin.php | 2 +- .../Product/Price/Plugin/CustomerGroup.php | 2 +- .../Indexer/Product/Price/Plugin/Website.php | 2 +- .../Model/Indexer/Product/Price/Processor.php | 2 +- .../Price/System/Config/PriceScope.php | 2 +- app/code/Magento/Catalog/Model/Layer.php | 2 +- .../Model/Layer/AvailabilityFlagInterface.php | 2 +- .../Magento/Catalog/Model/Layer/Category.php | 2 +- .../Model/Layer/Category/AvailabilityFlag.php | 2 +- .../Model/Layer/Category/CollectionFilter.php | 2 +- .../Category/FilterableAttributeList.php | 2 +- .../Layer/Category/ItemCollectionProvider.php | 2 +- .../Catalog/Model/Layer/Category/StateKey.php | 2 +- .../Model/Layer/CollectionFilterInterface.php | 2 +- .../Magento/Catalog/Model/Layer/Context.php | 2 +- .../Catalog/Model/Layer/ContextInterface.php | 2 +- .../Model/Layer/Filter/AbstractFilter.php | 2 +- .../Catalog/Model/Layer/Filter/Attribute.php | 2 +- .../Catalog/Model/Layer/Filter/Category.php | 2 +- .../Layer/Filter/DataProvider/Category.php | 2 +- .../Layer/Filter/DataProvider/Decimal.php | 2 +- .../Model/Layer/Filter/DataProvider/Price.php | 2 +- .../Catalog/Model/Layer/Filter/Decimal.php | 2 +- .../Layer/Filter/Dynamic/AlgorithmFactory.php | 2 +- .../Filter/Dynamic/AlgorithmInterface.php | 2 +- .../Model/Layer/Filter/Dynamic/Auto.php | 2 +- .../Model/Layer/Filter/Dynamic/Improved.php | 2 +- .../Model/Layer/Filter/Dynamic/Manual.php | 2 +- .../Catalog/Model/Layer/Filter/Factory.php | 2 +- .../Model/Layer/Filter/FilterInterface.php | 2 +- .../Catalog/Model/Layer/Filter/Item.php | 2 +- .../Model/Layer/Filter/Item/DataBuilder.php | 2 +- .../Catalog/Model/Layer/Filter/Price.php | 2 +- .../Model/Layer/Filter/Price/Range.php | 2 +- .../Model/Layer/Filter/Price/Render.php | 2 +- .../Catalog/Model/Layer/FilterList.php | 2 +- .../FilterableAttributeListInterface.php | 2 +- .../Layer/ItemCollectionProviderInterface.php | 2 +- .../Magento/Catalog/Model/Layer/Resolver.php | 2 +- .../Magento/Catalog/Model/Layer/Search.php | 2 +- .../Model/Layer/Search/CollectionFilter.php | 2 +- .../Model/Layer/Search/Filter/Attribute.php | 2 +- .../Layer/Search/FilterableAttributeList.php | 2 +- .../Layer/Search/ItemCollectionProvider.php | 2 +- .../Magento/Catalog/Model/Layer/State.php | 2 +- .../Catalog/Model/Layer/StateKeyInterface.php | 2 +- .../Model/Layout/DepersonalizePlugin.php | 2 +- .../Model/Locator/LocatorInterface.php | 2 +- .../Catalog/Model/Locator/RegistryLocator.php | 2 +- app/code/Magento/Catalog/Model/Plugin/Log.php | 2 +- .../ProductRepository/TransactionWrapper.php | 2 +- .../Model/Plugin/QuoteItemProductOption.php | 2 +- .../Model/Plugin/ShowOutOfStockConfig.php | 2 +- app/code/Magento/Catalog/Model/Product.php | 23 +- .../Magento/Catalog/Model/Product/Action.php | 2 +- .../Product/Attribute/AttributeSetFinder.php | 2 +- .../Product/Attribute/Backend/Boolean.php | 2 +- .../Product/Attribute/Backend/Category.php | 2 +- .../Backend/GroupPrice/AbstractGroupPrice.php | 16 +- .../Backend/Media/EntryConverterInterface.php | 2 +- .../Backend/Media/EntryConverterPool.php | 2 +- .../Backend/Media/ImageEntryConverter.php | 2 +- .../Model/Product/Attribute/Backend/Price.php | 82 +- .../Model/Product/Attribute/Backend/Sku.php | 7 +- .../Model/Product/Attribute/Backend/Stock.php | 4 +- .../Product/Attribute/Backend/Tierprice.php | 12 +- .../Product/Attribute/Backend/Weight.php | 2 +- .../Model/Product/Attribute/DataProvider.php | 2 +- .../Product/Attribute/DefaultAttributes.php | 2 +- .../Product/Attribute/Frontend/Image.php | 2 +- .../Catalog/Model/Product/Attribute/Group.php | 2 +- .../Model/Product/Attribute/Management.php | 2 +- .../Product/Attribute/OptionManagement.php | 2 +- .../Model/Product/Attribute/Repository.php | 52 +- .../Model/Product/Attribute/SetManagement.php | 2 +- .../Model/Product/Attribute/SetRepository.php | 2 +- .../Product/Attribute/Source/Boolean.php | 2 +- .../Attribute/Source/Countryofmanufacture.php | 26 +- .../Product/Attribute/Source/Inputtype.php | 2 +- .../Model/Product/Attribute/Source/Layout.php | 2 +- .../Model/Product/Attribute/Source/Status.php | 2 +- .../Catalog/Model/Product/Attribute/Type.php | 2 +- .../Model/Product/Attribute/TypesList.php | 2 +- .../Model/Product/AttributeSet/Build.php | 2 +- .../Model/Product/AttributeSet/Options.php | 2 +- .../Product/AttributeSet/SuggestedSet.php | 2 +- .../Model/Product/CartConfiguration.php | 2 +- .../Catalog/Model/Product/CatalogPrice.php | 2 +- .../Model/Product/CatalogPriceFactory.php | 2 +- .../Model/Product/CatalogPriceInterface.php | 2 +- .../Catalog/Model/Product/Compare/Item.php | 2 +- .../Model/Product/Compare/ListCompare.php | 2 +- .../Catalog/Model/Product/Condition.php | 2 +- .../Product/Condition/ConditionInterface.php | 2 +- .../Configuration/Item/ItemInterface.php | 2 +- .../Product/Configuration/Item/Option.php | 2 +- .../Item/Option/OptionInterface.php | 2 +- .../Magento/Catalog/Model/Product/Copier.php | 2 +- .../Product/CopyConstructor/Composite.php | 2 +- .../Product/CopyConstructor/CrossSell.php | 2 +- .../Model/Product/CopyConstructor/Related.php | 2 +- .../Model/Product/CopyConstructor/UpSell.php | 2 +- .../Model/Product/CopyConstructorFactory.php | 2 +- .../Product/CopyConstructorInterface.php | 2 +- .../Model/Product/Edit/WeightResolver.php | 2 +- .../Catalog/Model/Product/Exception.php | 2 +- .../Model/Product/Gallery/CreateHandler.php | 39 +- .../Catalog/Model/Product/Gallery/Entry.php | 2 +- .../Model/Product/Gallery/EntryResolver.php | 2 +- .../Product/Gallery/GalleryManagement.php | 7 +- .../Product/Gallery/MimeTypeExtensionMap.php | 2 +- .../Model/Product/Gallery/Processor.php | 6 +- .../Model/Product/Gallery/ReadHandler.php | 2 +- .../Model/Product/Gallery/UpdateHandler.php | 7 +- .../Magento/Catalog/Model/Product/Image.php | 202 +- .../Catalog/Model/Product/Image/Cache.php | 2 +- .../Initialization/Helper/ProductLinks.php | 2 +- .../Magento/Catalog/Model/Product/Link.php | 2 +- .../Catalog/Model/Product/Link/Converter.php | 2 +- .../Catalog/Model/Product/Link/Resolver.php | 2 +- .../Model/Product/Link/SaveHandler.php | 19 +- .../Model/Product/LinkTypeProvider.php | 2 +- .../Product/Media/AttributeManagement.php | 2 +- .../Catalog/Model/Product/Media/Config.php | 2 +- .../Model/Product/Media/ConfigInterface.php | 2 +- .../Magento/Catalog/Model/Product/Option.php | 2 +- .../Model/Product/Option/Converter.php | 2 +- .../Model/Product/Option/ReadHandler.php | 2 +- .../Model/Product/Option/Repository.php | 27 +- .../Model/Product/Option/SaveHandler.php | 22 +- .../Catalog/Model/Product/Option/Type.php | 2 +- .../Model/Product/Option/Type/Date.php | 17 +- .../Model/Product/Option/Type/DefaultType.php | 2 +- .../Model/Product/Option/Type/Factory.php | 2 +- .../Model/Product/Option/Type/File.php | 50 +- .../Option/Type/File/ValidateFactory.php | 2 +- .../Product/Option/Type/File/Validator.php | 2 +- .../Option/Type/File/ValidatorFile.php | 2 +- .../Option/Type/File/ValidatorInfo.php | 2 +- .../Model/Product/Option/Type/Select.php | 2 +- .../Model/Product/Option/Type/Text.php | 2 +- .../Model/Product/Option/UrlBuilder.php | 2 +- .../Option/Validator/DefaultValidator.php | 2 +- .../Model/Product/Option/Validator/File.php | 2 +- .../Model/Product/Option/Validator/Pool.php | 2 +- .../Model/Product/Option/Validator/Select.php | 5 +- .../Model/Product/Option/Validator/Text.php | 2 +- .../Catalog/Model/Product/Option/Value.php | 4 +- .../Catalog/Model/Product/Price/BasePrice.php | 79 + .../Model/Product/Price/BasePriceStorage.php | 228 + .../Catalog/Model/Product/Price/Cost.php | 79 + .../Model/Product/Price/CostStorage.php | 219 + .../Model/Product/Price/PricePersistence.php | 228 + .../Model/Product/Price/PriceUpdateResult.php | 64 + .../Model/Product/Price/SpecialPrice.php | 112 + .../Product/Price/SpecialPriceStorage.php | 298 + .../Catalog/Model/Product/Price/TierPrice.php | 127 + .../Model/Product/Price/TierPriceFactory.php | 169 + .../Product/Price/TierPricePersistence.php | 166 + .../Model/Product/Price/TierPriceStorage.php | 360 + .../Price/Validation/InvalidSkuProcessor.php | 78 + .../Model/Product/Price/Validation/Result.php | 82 + .../Price/Validation/TierPriceValidator.php | 478 ++ .../Catalog/Model/Product/PriceModifier.php | 2 +- .../Model/Product/PriceModifier/Composite.php | 2 +- .../Model/Product/PriceModifierInterface.php | 2 +- .../Pricing/Renderer/SalableResolver.php | 24 + .../Renderer/SalableResolverInterface.php | 21 + .../Model/Product/ProductList/Toolbar.php | 2 +- .../Model/Product/ReservedAttributeList.php | 2 +- .../Product/ScopedTierPriceManagement.php | 2 +- .../Catalog/Model/Product/TierPrice.php | 2 +- .../Model/Product/TierPriceManagement.php | 2 +- .../Magento/Catalog/Model/Product/Type.php | 2 +- .../Model/Product/Type/AbstractType.php | 31 +- .../Catalog/Model/Product/Type/Pool.php | 2 +- .../Catalog/Model/Product/Type/Price.php | 11 +- .../Model/Product/Type/Price/Factory.php | 2 +- .../Catalog/Model/Product/Type/Simple.php | 2 +- .../Catalog/Model/Product/Type/Virtual.php | 2 +- .../Model/Product/TypeTransitionManager.php | 2 +- .../Magento/Catalog/Model/Product/Url.php | 2 +- .../Catalog/Model/Product/Validator.php | 2 +- .../Catalog/Model/Product/Visibility.php | 2 +- .../Magento/Catalog/Model/Product/Website.php | 2 +- .../Model/Product/Website/ReadHandler.php | 2 +- .../Model/Product/Website/SaveHandler.php | 2 +- .../Model/ProductAttributeGroupRepository.php | 2 +- .../Catalog/Model/ProductIdLocator.php | 102 + .../Model/ProductIdLocatorInterface.php | 21 + .../Catalog/Model/ProductLink/Attribute.php | 2 +- .../Model/ProductLink/CollectionProvider.php | 16 +- .../CollectionProvider/Crosssell.php | 2 +- .../CollectionProvider/Related.php | 2 +- .../ProductLink/CollectionProvider/Upsell.php | 2 +- .../CollectionProviderInterface.php | 2 +- .../Converter/ConverterInterface.php | 2 +- .../ProductLink/Converter/ConverterPool.php | 2 +- .../Converter/DefaultConverter.php | 2 +- .../Catalog/Model/ProductLink/Link.php | 2 +- .../Catalog/Model/ProductLink/Management.php | 2 +- .../Catalog/Model/ProductLink/Repository.php | 2 +- .../Catalog/Model/ProductLink/Type.php | 2 +- .../Catalog/Model/ProductManagement.php | 2 +- .../Magento/Catalog/Model/ProductOption.php | 2 +- .../Catalog/Model/ProductOptionProcessor.php | 2 +- .../Model/ProductOptionProcessorInterface.php | 2 +- .../Catalog/Model/ProductOptions/Config.php | 17 +- .../Model/ProductOptions/Config/Converter.php | 2 +- .../Model/ProductOptions/Config/Reader.php | 2 +- .../ProductOptions/Config/SchemaLocator.php | 2 +- .../Model/ProductOptions/ConfigInterface.php | 2 +- .../Catalog/Model/ProductOptions/TypeList.php | 2 +- .../Catalog/Model/ProductRepository.php | 61 +- .../Magento/Catalog/Model/ProductType.php | 2 +- .../Magento/Catalog/Model/ProductTypeList.php | 2 +- .../Catalog/Model/ProductTypes/Config.php | 19 +- .../Model/ProductTypes/Config/Converter.php | 2 +- .../Model/ProductTypes/Config/Reader.php | 2 +- .../ProductTypes/Config/SchemaLocator.php | 2 +- .../Model/ProductTypes/ConfigInterface.php | 2 +- .../Catalog/Model/ProductWebsiteLink.php | 2 +- .../Model/ProductWebsiteLinkRepository.php | 6 +- .../ResourceModel/AbstractCollection.php | 2 +- .../Model/ResourceModel/AbstractResource.php | 4 +- .../Catalog/Model/ResourceModel/Attribute.php | 2 +- .../ResourceModel/AttributePersistor.php | 2 +- .../Catalog/Model/ResourceModel/Category.php | 50 +- .../ResourceModel/Category/AggregateCount.php | 2 +- .../Category/Attribute/Collection.php | 2 +- .../Category/Attribute/Frontend/Image.php | 2 +- .../Category/Attribute/Source/Layout.php | 2 +- .../Category/Attribute/Source/Page.php | 2 +- .../ResourceModel/Category/Collection.php | 2 +- .../Category/Collection/Factory.php | 2 +- .../Model/ResourceModel/Category/Flat.php | 2 +- .../Category/Flat/Collection.php | 2 +- .../Model/ResourceModel/Category/Tree.php | 2 +- .../Model/ResourceModel/CategoryProduct.php | 2 +- .../Collection/AbstractCollection.php | 2 +- .../Catalog/Model/ResourceModel/Config.php | 2 +- .../Model/ResourceModel/Eav/Attribute.php | 16 +- .../Catalog/Model/ResourceModel/Helper.php | 2 +- .../ResourceModel/Layer/Filter/Attribute.php | 2 +- .../ResourceModel/Layer/Filter/Decimal.php | 2 +- .../ResourceModel/Layer/Filter/Price.php | 2 +- .../MaxHeapTableSizeProcessor.php | 5 +- .../Catalog/Model/ResourceModel/Product.php | 15 +- .../Model/ResourceModel/Product/Action.php | 2 +- .../Backend/GroupPrice/AbstractGroupPrice.php | 2 +- .../Product/Attribute/Backend/Image.php | 2 +- .../Product/Attribute/Backend/Tierprice.php | 2 +- .../Product/Attribute/Collection.php | 2 +- .../Product/BaseSelectProcessorInterface.php | 26 + .../ResourceModel/Product/CategoryLink.php | 2 +- .../ResourceModel/Product/Collection.php | 59 +- .../Product/Collection/ProductLimitation.php | 2 +- .../ResourceModel/Product/Compare/Item.php | 2 +- .../Product/Compare/Item/Collection.php | 2 +- .../Product/CompositeBaseSelectProcessor.php | 49 + .../Model/ResourceModel/Product/Flat.php | 2 +- .../Model/ResourceModel/Product/Gallery.php | 19 +- .../Product/Indexer/AbstractIndexer.php | 2 +- .../Product/Indexer/Eav/AbstractEav.php | 18 +- .../Product/Indexer/Eav/Decimal.php | 5 +- .../Product/Indexer/Eav/Source.php | 79 +- ...LinkedProductSelectBuilderByIndexPrice.php | 31 +- .../Product/Indexer/Price/DefaultPrice.php | 30 +- .../Product/Indexer/Price/Factory.php | 2 +- .../Product/Indexer/Price/PriceInterface.php | 2 +- .../Model/ResourceModel/Product/Link.php | 26 +- .../ResourceModel/Product/Link/Collection.php | 2 +- .../Product/Link/DeleteHandler.php | 2 +- .../Product/Link/Product/Collection.php | 106 +- .../Product/Link/SaveHandler.php | 2 +- .../LinkedProductSelectBuilderByBasePrice.php | 37 +- ...nkedProductSelectBuilderBySpecialPrice.php | 40 +- .../LinkedProductSelectBuilderByTierPrice.php | 38 +- .../LinkedProductSelectBuilderComposite.php | 8 +- .../LinkedProductSelectBuilderInterface.php | 2 +- .../Model/ResourceModel/Product/Option.php | 30 +- .../Product/Option/Collection.php | 23 +- .../ResourceModel/Product/Option/Value.php | 30 +- .../Product/Option/Value/Collection.php | 2 +- .../Product/Price/SpecialPrice.php | 320 + .../Model/ResourceModel/Product/Relation.php | 2 +- .../Product/StatusBaseSelectProcessor.php | 81 + .../Model/ResourceModel/Product/Website.php | 2 +- .../ResourceModel/Product/Website/Link.php | 2 +- .../Product/Website/SelectProcessor.php | 67 + .../ResourceModel/Setup/PropertyMapper.php | 2 +- .../Catalog/Model/ResourceModel/Url.php | 2 +- .../Magento/Catalog/Model/Rss/Category.php | 2 +- .../Catalog/Model/Rss/Product/NewProducts.php | 2 +- .../Catalog/Model/Rss/Product/NotifyStock.php | 2 +- .../Catalog/Model/Rss/Product/Special.php | 2 +- app/code/Magento/Catalog/Model/Session.php | 2 +- .../Backend/Catalog/Url/Rewrite/Suffix.php | 48 +- .../Model/System/Config/Source/Inputtype.php | 2 +- .../Magento/Catalog/Model/Template/Filter.php | 7 +- .../Catalog/Model/Template/Filter/Factory.php | 2 +- .../Catalog/Model/View/Asset/Image.php | 206 + .../Model/View/Asset/Image/Context.php | 59 + .../Catalog/Model/View/Asset/Placeholder.php | 181 + .../Model/Webapi/Product/Option/Type/Date.php | 2 +- .../Product/Option/Type/File/Processor.php | 2 +- ...gCheckIsUsingStaticUrlsAllowedObserver.php | 2 +- .../Compare/BindCustomerLoginObserver.php | 2 +- .../Compare/BindCustomerLogoutObserver.php | 2 +- .../Catalog/Observer/MenuCategoryData.php | 2 +- .../Observer/SetSpecialPriceStartDate.php | 45 + ...witchPriceAttributeScopeOnConfigChange.php | 78 + .../Magento/Catalog/Plugin/Block/Topmenu.php | 2 +- .../Attribute/Backend/AttributeValidation.php | 2 +- .../Indexer/Category/Product/Execute.php | 2 +- ...MaxHeapTableSizeProcessorOnFullReindex.php | 5 +- .../Action/UpdateAttributesFlushCache.php | 2 +- .../Model/ResourceModel/Attribute/Save.php | 2 +- .../Plugin/Model/ResourceModel/Config.php | 23 +- .../Catalog/Pricing/Price/BasePrice.php | 2 +- .../Catalog/Pricing/Price/ConfiguredPrice.php | 2 +- .../Price/ConfiguredPriceInterface.php | 2 +- .../Pricing/Price/CustomOptionPrice.php | 2 +- .../Price/CustomOptionPriceInterface.php | 2 +- .../Catalog/Pricing/Price/FinalPrice.php | 2 +- .../Pricing/Price/FinalPriceInterface.php | 2 +- .../Price/MinimalPriceCalculatorInterface.php | 32 + .../Price/MinimalTierPriceCalculator.php | 63 + .../Catalog/Pricing/Price/RegularPrice.php | 2 +- .../Catalog/Pricing/Price/SpecialPrice.php | 2 +- .../Pricing/Price/SpecialPriceInterface.php | 2 +- .../Catalog/Pricing/Price/TierPrice.php | 2 +- .../Pricing/Price/TierPriceInterface.php | 2 +- app/code/Magento/Catalog/Pricing/Render.php | 2 +- .../Pricing/Render/ConfiguredPriceBox.php | 2 +- .../Catalog/Pricing/Render/FinalPriceBox.php | 95 +- .../Catalog/Pricing/Render/PriceBox.php | 2 +- .../Magento/Catalog/Setup/CategorySetup.php | 2 +- .../Magento/Catalog/Setup/InstallData.php | 2 +- .../Magento/Catalog/Setup/InstallSchema.php | 13 +- app/code/Magento/Catalog/Setup/Recurring.php | 2 +- .../Magento/Catalog/Setup/UpgradeData.php | 80 +- .../Magento/Catalog/Setup/UpgradeSchema.php | 169 +- .../Category/AbstractCategoryTest.php | 2 +- .../Product/Attribute/Button/CancelTest.php | 2 +- .../Product/Attribute/Button/GenericTest.php | 2 +- .../Product/Attribute/Button/SaveTest.php | 2 +- .../Attribute/Edit/Tab/AdvancedTest.php | 2 +- .../Adminhtml/Product/Attribute/GridTest.php | 2 +- .../Composite/Fieldset/OptionsTest.php | 2 +- .../Action/Attribute/Tab/InventoryTest.php | 2 +- .../Product/Edit/Button/AddAttributeTest.php | 2 +- .../Product/Edit/Button/BackTest.php | 2 +- .../Edit/Button/CreateCategoryTest.php | 2 +- .../Product/Edit/Button/GenericTest.php | 2 +- .../Product/Edit/Button/SaveTest.php | 2 +- .../Adminhtml/Product/Edit/Tab/AlertsTest.php | 2 +- .../Product/Edit/Tab/InventoryTest.php | 2 +- .../Product/Helper/Form/CategoryTest.php | 2 +- .../Helper/Form/Gallery/ContentTest.php | 2 +- .../Product/Helper/Form/GalleryTest.php | 2 +- .../Product/Helper/Form/WeightTest.php | 2 +- .../Adminhtml/Product/Options/AjaxTest.php | 15 +- .../Block/Adminhtml/Rss/Grid/LinkTest.php | 2 +- .../Block/Adminhtml/Rss/NotifyStockTest.php | 2 +- .../Category/Plugin/PriceBoxTagsTest.php | 2 +- .../Test/Unit/Block/Category/Rss/LinkTest.php | 2 +- .../Test/Unit/Block/Category/ViewTest.php | 2 +- .../Test/Unit/Block/NavigationTest.php | 2 +- .../Block/Product/AbstractProductTest.php | 2 +- .../Block/Product/Compare/ListCompareTest.php | 2 +- .../Test/Unit/Block/Product/ContextTest.php | 2 +- .../Unit/Block/Product/ImageBuilderTest.php | 2 +- .../Unit/Block/Product/ListProductTest.php | 10 +- .../Test/Unit/Block/Product/ListTest.php | 2 +- .../Unit/Block/Product/NewProductTest.php | 2 +- .../Test/Unit/Block/Product/PriceTest.php | 2 +- .../Block/Product/ProductList/RelatedTest.php | 2 +- .../Block/Product/ProductList/ToolbarTest.php | 2 +- .../Block/Product/ProductList/UpsellTest.php | 2 +- .../Unit/Block/Product/View/GalleryTest.php | 19 +- .../Unit/Block/Product/View/OptionsTest.php | 2 +- .../Test/Unit/Block/Product/View/TabsTest.php | 2 +- .../Test/Unit/Block/Product/ViewTest.php | 8 +- .../Block/Product/Widget/NewWidgetTest.php | 23 +- .../Test/Unit/Block/Rss/CategoryTest.php | 2 +- .../Block/Rss/Product/NewProductsTest.php | 2 +- .../Unit/Block/Rss/Product/SpecialTest.php | 2 +- .../Test/Unit/Block/Widget/LinkTest.php | 2 +- .../Command/ImagesResizeCommandTest.php | 2 +- .../Adminhtml/Category/DeleteTest.php | 2 +- .../Adminhtml/Category/EditTest.php | 2 +- .../Adminhtml/Category/Image/UploadTest.php | 68 + .../Adminhtml/Category/SaveTest.php | 183 +- .../Category/Widget/CategoriesJsonTest.php | 2 +- .../Adminhtml/Category/Widget/ChooserTest.php | 2 +- .../Product/Action/Attribute/EditTest.php | 2 +- .../Product/Action/Attribute/SaveTest.php | 2 +- .../Product/AddAttributeToTemplateTest.php | 247 + .../Adminhtml/Product/Attribute/EditTest.php | 2 +- .../Adminhtml/Product/Attribute/SaveTest.php | 2 +- .../Product/Attribute/ValidateTest.php | 2 +- .../Adminhtml/Product/AttributeTest.php | 2 +- .../Adminhtml/Product/BuilderTest.php | 2 +- .../Helper/HandlerFactoryTest.php | 2 +- .../Helper/Plugin/Handler/CompositeTest.php | 2 +- .../Product/Initialization/HelperTest.php | 292 +- .../Initialization/StockDataFilterTest.php | 2 +- .../Adminhtml/Product/MassStatusTest.php | 140 +- .../Adminhtml/Product/NewActionTest.php | 2 +- .../Adminhtml/Product/ReloadTest.php | 2 +- .../Controller/Adminhtml/Product/SaveTest.php | 2 +- .../Product/ShowUpdateResultTest.php | 2 +- .../Adminhtml/Product/ValidateTest.php | 2 +- .../Unit/Controller/Adminhtml/ProductTest.php | 28 +- .../Unit/Controller/Category/MoveTest.php | 320 + .../Unit/Controller/Category/ViewTest.php | 2 +- .../Controller/Product/Compare/IndexTest.php | 2 +- .../Unit/Cron/RefreshSpecialPricesTest.php | 2 +- .../Catalog/Test/Unit/Helper/ImageTest.php | 12 +- .../Test/Unit/Helper/Product/CompareTest.php | 25 +- .../Helper/Product/ConfigurationPoolTest.php | 2 +- .../Unit/Helper/Product/ConfigurationTest.php | 81 + .../Product/Edit/Action/AttributeTest.php | 2 +- .../Unit/Helper/Product/Flat/IndexerTest.php | 2 +- .../Catalog/Test/Unit/Helper/ProductTest.php | 2 +- .../ProductCategoryFilterTest.php | 2 +- .../Backend/CustomlayoutupdateTest.php | 2 +- .../Model/Attribute/Config/ConverterTest.php | 2 +- .../Model/Attribute/Config/ReaderTest.php | 2 +- .../Attribute/Config/SchemaLocatorTest.php | 2 +- .../Unit/Model/Attribute/Config/XsdTest.php | 2 +- .../_files/attributes_config_merged.php | 2 +- .../_files/attributes_config_merged.xml | 2 +- .../Config/_files/attributes_config_one.xml | 2 +- .../Config/_files/attributes_config_two.xml | 2 +- .../Test/Unit/Model/Attribute/ConfigTest.php | 2 +- .../Attribute/LockValidatorCompositeTest.php | 2 +- .../Category/Attribute/Backend/ImageTest.php | 293 + .../Category/Attribute/Backend/SortbyTest.php | 2 +- .../Category/Attribute/Source/LayoutTest.php | 2 +- .../Category/Attribute/Source/PageTest.php | 2 +- .../Category/Attribute/Source/SortbyTest.php | 2 +- .../Category/AttributeRepositoryTest.php | 2 +- .../Unit/Model/Category/DataProviderTest.php | 320 + .../Test/Unit/Model/Category/FileInfoTest.php | 114 + .../Model/Category/Link/ReadHandlerTest.php | 2 +- .../Model/Category/Link/SaveHandlerTest.php | 2 +- .../Test/Unit/Model/Category/TreeTest.php | 2 +- .../Unit/Model/CategoryLinkManagementTest.php | 2 +- .../Unit/Model/CategoryLinkRepositoryTest.php | 2 +- .../Test/Unit/Model/CategoryListTest.php | 2 +- .../Unit/Model/CategoryManagementTest.php | 2 +- .../Unit/Model/CategoryRepositoryTest.php | 2 +- .../Catalog/Test/Unit/Model/CategoryTest.php | 262 +- .../Unit/Model/CollectionProviderTest.php | 110 + .../Config/CatalogClone/Media/ImageTest.php | 2 +- .../Unit/Model/Config/Source/CategoryTest.php | 2 +- .../Model/Config/Source/GridPerPageTest.php | 2 +- .../Model/Config/Source/ListPerPageTest.php | 2 +- .../Unit/Model/Config/Source/ListSortTest.php | 2 +- .../Source/Product/Options/TypeTest.php | 2 +- .../Catalog/Test/Unit/Model/ConfigTest.php | 2 +- .../CustomOptionProcessorTest.php | 21 +- .../Model/CustomOptions/CustomOptionTest.php | 2 +- .../Test/Unit/Model/Entity/AttributeTest.php | 2 +- .../Catalog/Test/Unit/Model/FactoryTest.php | 2 +- .../Test/Unit/Model/ImageExtractorTest.php | 2 +- .../Flat/Plugin/IndexerConfigDataTest.php | 2 +- .../Category/Flat/Plugin/StoreGroupTest.php | 2 +- .../Category/Flat/Plugin/StoreViewTest.php | 2 +- .../Model/Indexer/Category/Flat/StateTest.php | 2 +- .../Category/Flat/System/Config/ModeTest.php | 2 +- .../Unit/Model/Indexer/Category/FlatTest.php | 2 +- .../Category/Product/Plugin/ImportTest.php | 2 +- .../Product/Plugin/MviewStateTest.php | 2 +- .../Product/Plugin/StoreGroupTest.php | 2 +- .../Category/Product/Plugin/StoreViewTest.php | 2 +- .../Model/Indexer/Category/ProductTest.php | 2 +- .../Product/Category/Plugin/ImportTest.php | 2 +- .../Model/Indexer/Product/CategoryTest.php | 2 +- .../Product/Eav/AbstractActionTest.php | 2 +- .../Indexer/Product/Eav/Action/FullTest.php | 2 +- .../Indexer/Product/Eav/Action/RowTest.php | 2 +- .../Indexer/Product/Eav/Action/RowsTest.php | 2 +- .../IndexableAttributeFilterTest.php | 2 +- .../Product/Eav/Plugin/AttributeSetTest.php | 2 +- .../Indexer/Product/Eav/Plugin/ImportTest.php | 2 +- .../Product/Eav/Plugin/StoreViewTest.php | 2 +- .../Unit/Model/Indexer/Product/EavTest.php | 2 +- .../Product/Flat/Action/EraserTest.php | 2 +- .../Indexer/Product/Flat/Action/RowTest.php | 2 +- .../Flat/Action/Rows/TableDataTest.php | 2 +- .../Indexer/Product/Flat/Action/RowsTest.php | 2 +- .../Product/Flat/FlatTableBuilderTest.php | 205 + .../Flat/Plugin/IndexerConfigDataTest.php | 2 +- .../Product/Flat/Plugin/StoreGroupTest.php | 2 +- .../Indexer/Product/Flat/Plugin/StoreTest.php | 2 +- .../Indexer/Product/Flat/ProcessorTest.php | 2 +- .../Model/Indexer/Product/Flat/StateTest.php | 2 +- .../Product/Flat/System/Config/ModeTest.php | 2 +- .../Product/Flat/Table/BuilderTest.php | 47 + .../Indexer/Product/Flat/TableDataTest.php | 2 +- .../Unit/Model/Indexer/Product/FlatTest.php | 2 +- .../Indexer/Product/Price/Action/RowTest.php | 2 +- .../Indexer/Product/Price/Action/RowsTest.php | 2 +- .../Price/Plugin/CustomerGroupTest.php | 2 +- .../Product/Price/Plugin/WebsiteTest.php | 2 +- .../Price/System/Config/PriceScopeTest.php | 2 +- .../Layer/Category/AvailabilityFlagTest.php | 2 +- .../Layer/Category/CollectionFilterTest.php | 2 +- .../Category/FilterableAttributeListTest.php | 2 +- .../Model/Layer/Category/StateKeyTest.php | 2 +- .../Unit/Model/Layer/Filter/AttributeTest.php | 2 +- .../Unit/Model/Layer/Filter/CategoryTest.php | 2 +- .../Filter/DataProvider/CategoryTest.php | 2 +- .../Layer/Filter/DataProvider/DecimalTest.php | 2 +- .../Layer/Filter/DataProvider/PriceTest.php | 2 +- .../Unit/Model/Layer/Filter/DecimalTest.php | 2 +- .../Unit/Model/Layer/Filter/FactoryTest.php | 2 +- .../Layer/Filter/Item/DataBuilderTest.php | 2 +- .../Unit/Model/Layer/Filter/PriceTest.php | 2 +- .../Test/Unit/Model/Layer/FilterListTest.php | 2 +- .../Layer/Search/CollectionFilterTest.php | 2 +- .../Search/FilterableAttributeListTest.php | 2 +- .../Unit/Model/Layer/Search/StateKeyTest.php | 2 +- .../Test/Unit/Model/Layer/StateTest.php | 2 +- .../Catalog/Test/Unit/Model/LayerTest.php | 2 +- .../Model/Layout/DepersonalizePluginTest.php | 2 +- .../Model/Locator/RegistryLocatorTest.php | 2 +- .../Test/Unit/Model/Plugin/LogTest.php | 2 +- .../TransactionWrapperTest.php | 2 +- .../Plugin/QuoteItemProductOptionTest.php | 2 +- .../Test/Unit/Model/Product/ActionTest.php | 2 +- .../Attribute/AttributeSetFinderTest.php | 2 +- .../Product/Attribute/Backend/BooleanTest.php | 2 +- .../Attribute/Backend/CategoryTest.php | 2 +- .../Backend/GroupPrice/AbstractTest.php | 12 +- .../Backend/Media/EntryConverterPoolTest.php | 2 +- .../Backend/Media/ImageEntryConverterTest.php | 2 +- .../Product/Attribute/Backend/PriceTest.php | 141 +- .../Product/Attribute/Backend/StockTest.php | 15 +- .../Product/Attribute/Backend/WeightTest.php | 2 +- .../Product/Attribute/Frontend/ImageTest.php | 2 +- .../Model/Product/Attribute/GroupTest.php | 2 +- .../Product/Attribute/ManagementTest.php | 2 +- .../Attribute/OptionManagementTest.php | 2 +- .../Product/Attribute/RepositoryTest.php | 41 +- .../Product/Attribute/SetManagementTest.php | 2 +- .../Product/Attribute/SetRepositoryTest.php | 2 +- .../Product/Attribute/Source/BooleanTest.php | 2 +- .../Source/CountryofmanufactureTest.php | 41 +- .../Attribute/Source/InputtypeTest.php | 2 +- .../Product/Attribute/Source/LayoutTest.php | 2 +- .../Product/Attribute/Source/StatusTest.php | 2 +- .../Model/Product/Attribute/TypesListTest.php | 2 +- .../Model/Product/CartConfigurationTest.php | 2 +- .../Unit/Model/Product/CatalogPriceTest.php | 2 +- .../Unit/Model/Product/Compare/ItemTest.php | 2 +- .../Test/Unit/Model/Product/ConditionTest.php | 2 +- .../Test/Unit/Model/Product/CopierTest.php | 2 +- .../Product/CopyConstructor/CompositeTest.php | 2 +- .../Product/CopyConstructor/CrossSellTest.php | 2 +- .../Product/CopyConstructor/RelatedTest.php | 2 +- .../Product/CopyConstructor/UpSellTest.php | 2 +- .../Product/CopyConstructorFactoryTest.php | 2 +- .../Product/Gallery/GalleryManagementTest.php | 21 +- .../Gallery/MimeTypeExtensionMapTest.php | 2 +- .../Model/Product/Gallery/ProcessorTest.php | 54 +- .../Unit/Model/Product/Image/CacheTest.php | 2 +- .../Test/Unit/Model/Product/ImageTest.php | 139 +- .../Helper/ProductLinksTest.php | 2 +- .../Unit/Model/Product/Link/ConverterTest.php | 2 +- .../Unit/Model/Product/Link/ResolverTest.php | 2 +- .../Test/Unit/Model/Product/LinkTest.php | 2 +- .../Model/Product/LinkTypeProviderTest.php | 2 +- .../Product/Media/AttributeManagementTest.php | 2 +- .../Model/Product/Option/RepositoryTest.php | 95 +- .../Model/Product/Option/SaveHandlerTest.php | 72 + .../Model/Product/Option/Type/FactoryTest.php | 2 +- .../Model/Product/Option/Type/FileTest.php | 164 +- .../Model/Product/Option/UrlBuilderTest.php | 2 +- .../Option/Validator/DefaultValidatorTest.php | 2 +- .../Product/Option/Validator/FileTest.php | 2 +- .../Product/Option/Validator/PoolTest.php | 2 +- .../Product/Option/Validator/SelectTest.php | 2 +- .../Product/Option/Validator/TextTest.php | 2 +- .../Unit/Model/Product/Option/ValueTest.php | 2 +- .../Test/Unit/Model/Product/OptionTest.php | 2 +- .../Product/Price/BasePriceStorageTest.php | 286 + .../Model/Product/Price/CostStorageTest.php | 311 + .../Product/Price/PricePersistenceTest.php | 365 + .../Product/Price/SpecialPriceStorageTest.php | 385 + .../Product/Price/TierPriceStorageTest.php | 300 + .../Validation/InvalidSkuProcessorTest.php | 89 + .../Validation/TierPriceValidatorTest.php | 260 + .../Product/PriceModifier/CompositeTest.php | 2 +- .../Unit/Model/Product/PriceModifierTest.php | 2 +- .../Pricing/Renderer/SalableResolverTest.php | 56 + .../Model/Product/ProductList/ToolbarTest.php | 2 +- .../Product/ReservedAttributeListTest.php | 2 +- .../Model/Product/TierPriceManagementTest.php | 2 +- .../Model/Product/Type/AbstractTypeTest.php | 2 +- .../Unit/Model/Product/Type/PriceTest.php | 2 +- .../Unit/Model/Product/Type/SimpleTest.php | 2 +- .../Unit/Model/Product/Type/VirtualTest.php | 2 +- .../Test/Unit/Model/Product/TypeTest.php | 2 +- .../Product/TypeTransitionManagerTest.php | 2 +- .../Test/Unit/Model/Product/UrlTest.php | 2 +- .../Test/Unit/Model/Product/ValidatorTest.php | 2 +- .../Unit/Model/Product/VisibilityTest.php | 2 +- .../Model/Product/Website/ReadHandlerTest.php | 2 +- .../Model/Product/Website/SaveHandlerTest.php | 2 +- .../ProductAttributeGroupRepositoryTest.php | 2 +- .../Test/Unit/Model/ProductIdLocatorTest.php | 87 + .../Unit/Model/ProductLink/ManagementTest.php | 2 +- .../Unit/Model/ProductLink/RepositoryTest.php | 2 +- .../Test/Unit/Model/ProductManagementTest.php | 2 +- .../Unit/Model/ProductOptionProcessorTest.php | 2 +- .../Model/ProductOptions/Config/XsdTest.php | 2 +- .../invalidProductOptionsMergedXmlArray.php | 2 +- .../_files/invalidProductOptionsXmlArray.php | 2 +- .../_files/product_options_merged_valid.xml | 2 +- .../Config/_files/product_options_valid.xml | 2 +- .../Test/Unit/Model/ProductRepositoryTest.php | 94 +- .../Catalog/Test/Unit/Model/ProductTest.php | 36 +- .../Test/Unit/Model/ProductTypeListTest.php | 2 +- .../ProductTypes/Config/ConverterTest.php | 2 +- .../ProductTypes/Config/SchemaLocatorTest.php | 2 +- .../ProductTypes/Config/XsdMergedTest.php | 2 +- .../Model/ProductTypes/Config/XsdTest.php | 2 +- .../invalidProductTypesMergedXmlArray.php | 2 +- .../_files/invalidProductTypesXmlArray.php | 2 +- .../Config/_files/product_types.php | 2 +- .../Config/_files/product_types.xml | 2 +- .../Config/_files/valid_product_types.xml | 2 +- .../_files/valid_product_types_merged.xml | 2 +- .../Unit/Model/ProductTypes/ConfigTest.php | 92 +- .../Unit/Model/ResourceModel/AbstractTest.php | 2 +- .../Category/Collection/FactoryTest.php | 2 +- .../Model/ResourceModel/Category/FlatTest.php | 2 +- .../Model/ResourceModel/Category/TreeTest.php | 2 +- .../Model/ResourceModel/Eav/AttributeTest.php | 2 +- .../Product/CategoryLinkTest.php | 2 +- .../Collection/ProductLimitationTest.php | 2 +- .../ResourceModel/Product/CollectionTest.php | 137 +- .../CompositeBaseSelectProcessorTest.php | 54 + .../Model/ResourceModel/Product/FlatTest.php | 2 +- .../ResourceModel/Product/GalleryTest.php | 31 +- .../Product/Link/Product/CollectionTest.php | 41 +- .../Model/ResourceModel/Product/LinkTest.php | 2 +- .../Product/Option/CollectionTest.php | 42 +- .../Product/StatusBaseSelectProcessorTest.php | 129 + .../Product/Website/LinkTest.php | 2 +- .../Unit/Model/ResourceModel/ProductTest.php | 2 +- .../Test/Unit/Model/Rss/CategoryTest.php | 2 +- .../Model/Rss/Product/NewProductsTest.php | 2 +- .../Model/Rss/Product/NotifyStockTest.php | 2 +- .../Unit/Model/Rss/Product/SpecialTest.php | 2 +- .../Catalog/Url/Rewrite/SuffixTest.php | 2 +- .../System/Config/Source/InputtypeTest.php | 2 +- .../Model/Template/Filter/FactoryTest.php | 2 +- .../Model/View/Asset/Image/ContextTest.php | 76 + .../Test/Unit/Model/View/Asset/ImageTest.php | 172 + .../Unit/Model/View/Asset/PlaceholderTest.php | 163 + .../Test/Unit/Model/_files/converted_view.php | 2 +- .../Test/Unit/Model/_files/valid_view.xml | 2 +- .../Unit/Observer/MenuCategoryDataTest.php | 2 +- .../Test/Unit/Plugin/Block/TopmenuTest.php | 2 +- .../Indexer/Category/Product/ExecuteTest.php | 2 +- .../Action/UpdateAttributesFlushCacheTest.php | 2 +- .../ResourceModel/Attribute/SaveTest.php | 2 +- .../Plugin/Model/ResourceModel/ConfigTest.php | 52 +- .../Test/Unit/Pricing/Price/BasePriceTest.php | 2 +- .../Pricing/Price/ConfiguredPriceTest.php | 2 +- .../Pricing/Price/CustomOptionPriceTest.php | 2 +- .../Unit/Pricing/Price/FinalPriceTest.php | 2 +- .../Price/MinimalTierPriceCalculatorTest.php | 112 + .../Unit/Pricing/Price/RegularPriceTest.php | 2 +- .../Unit/Pricing/Price/SpecialPriceTest.php | 2 +- .../Test/Unit/Pricing/Price/TierPriceTest.php | 2 +- .../Unit/Pricing/Render/FinalPriceBoxTest.php | 110 +- .../Test/Unit/Pricing/Render/PriceBoxTest.php | 2 +- .../Catalog/Test/Unit/Pricing/RenderTest.php | 2 +- .../Test/Unit/Setup/CategorySetupTest.php | 2 +- .../Test/Unit/Ui/AllowedProductTypesTest.php | 2 +- .../Listing/Columns/AbstractColumnTest.php | 2 +- .../Listing/Columns/AttributeSetTextTest.php | 2 +- .../Listing/Columns/StatusTextTest.php | 2 +- .../Product/Form/Categories/OptionsTest.php | 2 +- .../CatalogEavValidationRulesTest.php | 2 +- .../Form/Modifier/AbstractModifierTest.php | 2 +- .../Form/Modifier/AdvancedPricingTest.php | 2 +- .../Form/Modifier/AttributeSetTest.php | 2 +- .../Product/Form/Modifier/AttributesTest.php | 2 +- .../Product/Form/Modifier/CategoriesTest.php | 2 +- .../Form/Modifier/CustomOptionsTest.php | 46 +- .../Product/Form/Modifier/EavTest.php | 2 +- .../Product/Form/Modifier/FactoryTest.php | 2 +- .../Product/Form/Modifier/GeneralTest.php | 2 +- .../Product/Form/Modifier/ImagesTest.php | 26 +- .../Product/Form/Modifier/RelatedTest.php | 2 +- .../Modifier/ScheduleDesignUpdateTest.php | 2 +- .../Product/Form/Modifier/SystemTest.php | 2 +- .../Product/Form/Modifier/TierPriceTest.php | 141 + .../Product/Form/Modifier/WebsitesTest.php | 2 +- .../Form/NewCategoryDataProviderTest.php | 2 +- .../Product/Form/ProductDataProviderTest.php | 2 +- .../ProductCustomOptionsDataProviderTest.php | 2 +- .../Related/AbstractDataProviderTest.php | 2 +- .../Related/CrossSellDataProviderTest.php | 2 +- .../Related/RelatedDataProviderTest.php | 2 +- .../Related/UpSellDataProviderTest.php | 2 +- .../Catalog/Ui/AllowedProductTypes.php | 2 +- .../Category/Form/Element/Wysiwyg.php | 2 +- .../Catalog/Ui/Component/ColumnFactory.php | 2 +- .../Catalog/Ui/Component/FilterFactory.php | 2 +- .../Listing/Attribute/AbstractRepository.php | 2 +- .../Listing/Attribute/Repository.php | 2 +- .../Listing/Attribute/RepositoryInterface.php | 2 +- .../Catalog/Ui/Component/Listing/Columns.php | 2 +- .../Listing/Columns/AttributeSetText.php | 2 +- .../Ui/Component/Listing/Columns/Price.php | 2 +- .../Listing/Columns/ProductActions.php | 2 +- .../Component/Listing/Columns/StatusText.php | 2 +- .../Component/Listing/Columns/Thumbnail.php | 2 +- .../Ui/Component/Listing/Columns/Websites.php | 2 +- .../Catalog/Ui/Component/Listing/Filters.php | 2 +- .../Product/Form/Categories/Options.php | 2 +- .../CatalogEavValidationRules.php | 2 +- .../Product/AddStoreFieldToCollection.php | 2 +- .../Product/AddWebsitesFieldToCollection.php | 2 +- .../Product/Attributes/Listing.php | 2 +- .../Form/Modifier/AbstractModifier.php | 2 +- .../Product/Form/Modifier/AdvancedPricing.php | 4 +- .../Product/Form/Modifier/AttributeSet.php | 2 +- .../Product/Form/Modifier/Attributes.php | 2 +- .../Product/Form/Modifier/Categories.php | 17 +- .../Product/Form/Modifier/CustomOptions.php | 19 +- .../Product/Form/Modifier/Eav.php | 7 +- .../Product/Form/Modifier/General.php | 13 +- .../Product/Form/Modifier/Images.php | 17 +- .../Product/Form/Modifier/Related.php | 2 +- .../Form/Modifier/ScheduleDesignUpdate.php | 2 +- .../Product/Form/Modifier/System.php | 2 +- .../Product/Form/Modifier/TierPrice.php | 172 + .../Product/Form/Modifier/Websites.php | 2 +- .../Product/Form/NewCategoryDataProvider.php | 2 +- .../Product/Form/ProductDataProvider.php | 2 +- .../ProductCustomOptionsDataProvider.php | 2 +- .../Product/ProductDataProvider.php | 2 +- .../Product/Related/AbstractDataProvider.php | 2 +- .../Product/Related/CrossSellDataProvider.php | 2 +- .../Product/Related/RelatedDataProvider.php | 2 +- .../Product/Related/UpSellDataProvider.php | 2 +- app/code/Magento/Catalog/composer.json | 2 +- app/code/Magento/Catalog/etc/acl.xml | 2 +- app/code/Magento/Catalog/etc/adminhtml/di.xml | 17 +- .../Magento/Catalog/etc/adminhtml/events.xml | 2 +- .../Magento/Catalog/etc/adminhtml/menu.xml | 2 +- .../Magento/Catalog/etc/adminhtml/routes.xml | 2 +- .../Magento/Catalog/etc/adminhtml/system.xml | 2 +- .../Catalog/etc/catalog_attributes.xml | 2 +- .../Catalog/etc/catalog_attributes.xsd | 2 +- app/code/Magento/Catalog/etc/config.xml | 2 +- app/code/Magento/Catalog/etc/crontab.xml | 5 +- app/code/Magento/Catalog/etc/di.xml | 80 +- .../Magento/Catalog/etc/eav_attributes.xml | 3 +- app/code/Magento/Catalog/etc/events.xml | 8 +- .../Catalog/etc/extension_attributes.xml | 2 +- app/code/Magento/Catalog/etc/frontend/di.xml | 5 +- .../Magento/Catalog/etc/frontend/events.xml | 2 +- .../Catalog/etc/frontend/page_types.xml | 2 +- .../Magento/Catalog/etc/frontend/routes.xml | 4 +- .../Magento/Catalog/etc/frontend/sections.xml | 2 +- app/code/Magento/Catalog/etc/indexer.xml | 2 +- app/code/Magento/Catalog/etc/module.xml | 4 +- app/code/Magento/Catalog/etc/mview.xml | 2 +- .../Magento/Catalog/etc/product_options.xml | 2 +- .../Magento/Catalog/etc/product_options.xsd | 2 +- .../Catalog/etc/product_options_merged.xsd | 2 +- .../Magento/Catalog/etc/product_types.xml | 2 +- .../Magento/Catalog/etc/product_types.xsd | 2 +- .../Catalog/etc/product_types_base.xsd | 2 +- .../Catalog/etc/product_types_merged.xsd | 2 +- app/code/Magento/Catalog/etc/view.xml | 2 +- app/code/Magento/Catalog/etc/webapi.xml | 74 +- .../Magento/Catalog/etc/webapi_rest/di.xml | 2 +- .../Magento/Catalog/etc/webapi_soap/di.xml | 2 +- app/code/Magento/Catalog/etc/widget.xml | 2 +- app/code/Magento/Catalog/i18n/en_US.csv | 2 +- app/code/Magento/Catalog/registration.php | 2 +- .../CATALOG_PRODUCT_COMPOSITE_CONFIGURE.xml | 2 +- ...ALOG_PRODUCT_COMPOSITE_CONFIGURE_ERROR.xml | 2 +- ...ATALOG_PRODUCT_COMPOSITE_UPDATE_RESULT.xml | 2 +- .../adminhtml/layout/catalog_category_add.xml | 2 +- .../layout/catalog_category_create.xml | 2 +- .../layout/catalog_category_edit.xml | 2 +- .../catalog_product_action_attribute_edit.xml | 2 +- .../catalog_product_alertspricegrid.xml | 2 +- .../catalog_product_alertsstockgrid.xml | 2 +- .../layout/catalog_product_attribute_edit.xml | 2 +- .../catalog_product_attribute_edit_form.xml | 2 +- .../catalog_product_attribute_edit_popup.xml | 2 +- .../catalog_product_change_attribute_set.xml | 2 +- .../layout/catalog_product_crosssell.xml | 2 +- .../layout/catalog_product_crosssellgrid.xml | 2 +- .../layout/catalog_product_customoptions.xml | 2 +- .../adminhtml/layout/catalog_product_edit.xml | 2 +- .../adminhtml/layout/catalog_product_form.xml | 2 +- .../adminhtml/layout/catalog_product_grid.xml | 2 +- .../layout/catalog_product_index.xml | 2 +- .../adminhtml/layout/catalog_product_new.xml | 2 +- .../layout/catalog_product_options.xml | 2 +- .../catalog_product_optionsimportgrid.xml | 2 +- .../layout/catalog_product_related.xml | 2 +- .../layout/catalog_product_relatedgrid.xml | 2 +- .../layout/catalog_product_reload.xml | 2 +- .../layout/catalog_product_set_block.xml | 2 +- .../layout/catalog_product_set_edit.xml | 2 +- .../layout/catalog_product_set_index.xml | 2 +- .../layout/catalog_product_upsell.xml | 2 +- .../layout/catalog_product_upsellgrid.xml | 2 +- .../view/adminhtml/requirejs-config.js | 4 +- .../catalog/category/checkboxes/tree.phtml | 2 +- .../templates/catalog/category/edit.phtml | 2 +- .../category/edit/assign_products.phtml | 2 +- .../templates/catalog/category/tree.phtml | 2 +- .../catalog/category/widget/tree.phtml | 2 +- .../form/renderer/fieldset/element.phtml | 2 +- .../adminhtml/templates/catalog/product.phtml | 2 +- .../catalog/product/attribute/form.phtml | 2 +- .../catalog/product/attribute/js.phtml | 3 +- .../catalog/product/attribute/labels.phtml | 44 +- .../catalog/product/attribute/options.phtml | 8 +- .../catalog/product/attribute/set/main.phtml | 62 +- .../attribute/set/main/tree/attribute.phtml | 2 +- .../attribute/set/main/tree/group.phtml | 2 +- .../product/attribute/set/toolbar/add.phtml | 2 +- .../product/attribute/set/toolbar/main.phtml | 2 +- .../catalog/product/composite/configure.phtml | 2 +- .../product/composite/fieldset/options.phtml | 2 +- .../composite/fieldset/options/js.phtml | 2 +- .../fieldset/options/type/date.phtml | 2 +- .../fieldset/options/type/default.phtml | 2 +- .../fieldset/options/type/file.phtml | 2 +- .../fieldset/options/type/select.phtml | 2 +- .../fieldset/options/type/text.phtml | 2 +- .../product/composite/fieldset/qty.phtml | 2 +- .../templates/catalog/product/edit.phtml | 2 +- .../product/edit/action/attribute.phtml | 2 +- .../product/edit/action/inventory.phtml | 2 +- .../product/edit/action/websites.phtml | 2 +- .../catalog/product/edit/attribute_set.phtml | 2 +- .../product/edit/category/new/form.phtml | 2 +- .../catalog/product/edit/options.phtml | 2 +- .../catalog/product/edit/options/option.phtml | 2 +- .../product/edit/options/type/date.phtml | 2 +- .../product/edit/options/type/file.phtml | 2 +- .../product/edit/options/type/select.phtml | 2 +- .../product/edit/options/type/text.phtml | 2 +- .../catalog/product/edit/price/tier.phtml | 2 +- .../catalog/product/edit/serializer.phtml | 2 +- .../catalog/product/edit/websites.phtml | 2 +- .../catalog/product/helper/gallery.phtml | 2 +- .../templates/catalog/product/js.phtml | 2 +- .../templates/catalog/product/tab/alert.phtml | 2 +- .../catalog/product/tab/inventory.phtml | 2 +- .../product/widget/chooser/container.phtml | 2 +- .../templates/catalog/wysiwyg/js.phtml | 2 +- .../product/edit/attribute/search.phtml | 2 +- .../templates/product/edit/tabs.phtml | 2 +- .../product/edit/tabs/child_tab.phtml | 2 +- .../product/grid/massaction_extended.phtml | 2 +- .../adminhtml/templates/rss/grid/link.phtml | 2 +- .../adminhtml/ui_component/category_form.xml | 35 +- .../crosssell_product_listing.xml | 2 +- .../ui_component/design_config_form.xml | 2 +- .../ui_component/new_category_form.xml | 2 +- .../product_attribute_add_form.xml | 2 +- .../ui_component/product_attributes_grid.xml | 2 +- .../product_custom_options_listing.xml | 2 +- .../adminhtml/ui_component/product_form.xml | 2 +- .../ui_component/product_listing.xml | 5 +- .../ui_component/related_product_listing.xml | 2 +- .../ui_component/upsell_product_listing.xml | 2 +- .../web/catalog/apply-to-type-switcher.js | 2 +- .../web/catalog/base-image-uploader.js | 77 +- .../web/catalog/category/assign-products.js | 2 +- .../adminhtml/web/catalog/category/edit.js | 106 +- .../adminhtml/web/catalog/category/form.js | 4 +- .../web/catalog/product-attributes.js | 34 +- .../view/adminhtml/web/catalog/product.js | 63 +- .../product/attribute/unique-validate.js | 2 +- .../catalog/product/composite/configure.js | 3 +- .../view/adminhtml/web/catalog/type-events.js | 2 +- .../web/component/file-type-field.js | 2 +- .../web/component/image-size-field.js | 5 +- .../web/component/select-type-grid.js | 2 +- .../web/component/static-type-container.js | 2 +- .../web/component/static-type-input.js | 2 +- .../web/component/static-type-select.js | 2 +- .../web/component/text-type-field.js | 2 +- .../adminhtml/web/js/bundle-proxy-button.js | 2 +- .../view/adminhtml/web/js/category-tree.js | 125 +- .../web/js/components/attribute-set-select.js | 2 +- .../web/js/components/attributes-fieldset.js | 2 +- .../js/components/attributes-grid-paging.js | 2 +- .../components/attributes-insert-listing.js | 2 +- .../adminhtml/web/js/components/checkbox.js | 2 +- .../web/js/components/disable-hide-select.js | 2 +- .../js/components/disable-on-option/input.js | 2 +- .../js/components/disable-on-option/select.js | 2 +- .../components/disable-on-option/strategy.js | 2 +- .../js/components/disable-on-option/yesno.js | 2 +- .../dynamic-rows-import-custom-options.js | 20 +- .../js/components/dynamic-rows-tier-price.js | 29 + .../web/js/components/import-handler.js | 171 +- .../js/components/input-handle-required.js | 2 +- .../adminhtml/web/js/components/messages.js | 2 +- .../components/multiselect-handle-required.js | 2 +- .../web/js/components/new-attribute-form.js | 2 +- .../components/new-attribute-insert-form.js | 2 +- .../web/js/components/new-category.js | 2 +- .../web/js/components/product-status.js | 2 +- .../js/components/select-handle-required.js | 2 +- .../web/js/components/select-to-checkbox.js | 2 +- .../js/components/url-key-handle-changes.js | 2 +- .../js/components/visible-on-option/date.js | 2 +- .../components/visible-on-option/fieldset.js | 2 +- .../js/components/visible-on-option/input.js | 2 +- .../js/components/visible-on-option/select.js | 2 +- .../components/visible-on-option/strategy.js | 2 +- .../components/visible-on-option/textarea.js | 2 +- .../js/components/visible-on-option/yesno.js | 2 +- .../adminhtml/web/js/custom-options-type.js | 2 +- .../view/adminhtml/web/js/custom-options.js | 137 +- .../view/adminhtml/web/js/edit-tree.js | 2 +- .../web/js/form/element/action-delete.js | 2 +- .../adminhtml/web/js/form/element/checkbox.js | 2 +- .../adminhtml/web/js/form/element/input.js | 2 +- .../web/js/form/element/price-input.js | 15 - .../adminhtml/web/js/new-category-dialog.js | 44 +- .../Catalog/view/adminhtml/web/js/options.js | 2 +- .../view/adminhtml/web/js/product-gallery.js | 76 +- .../web/js/product/weight-handler.js | 2 +- .../web/js/tier-price/percentage-processor.js | 73 + .../web/js/tier-price/value-type-select.js | 118 + .../js/utils/percentage-price-calculator.js | 45 + .../web/template/attributes/grid/paging.html | 2 +- .../view/adminhtml/web/template/checkbox.html | 2 +- .../template/form/element/action-delete.html | 4 +- .../element/helper/custom-option-service.html | 2 +- .../helper/custom-option-type-service.html | 4 +- .../web/template/form/element/input.html | 2 +- .../template/form/element/price-input.html | 22 - .../adminhtml/web/template/image-preview.html | 3 +- .../base/layout/catalog_product_prices.xml | 2 +- .../Catalog/view/base/layout/default.xml | 2 +- .../Catalog/view/base/layout/empty.xml | 2 +- .../view/base/templates/js/components.phtml | 2 +- .../product/price/amount/default.phtml | 6 +- .../product/price/configured_price.phtml | 2 +- .../templates/product/price/default.phtml | 2 +- .../templates/product/price/final_price.phtml | 2 +- .../templates/product/price/tier_prices.phtml | 2 +- .../Catalog/view/base/web/js/price-box.js | 2 +- .../view/base/web/js/price-option-date.js | 85 +- .../view/base/web/js/price-option-file.js | 5 +- .../Catalog/view/base/web/js/price-options.js | 124 +- .../Catalog/view/base/web/js/price-utils.js | 70 +- .../frontend/layout/catalog_category_view.xml | 49 +- .../catalog_category_view_type_default.xml | 2 +- ...ory_view_type_default_without_children.xml | 2 +- .../layout/catalog_product_compare_index.xml | 2 +- .../layout/catalog_product_gallery.xml | 2 +- .../layout/catalog_product_opengraph.xml | 2 +- .../frontend/layout/catalog_product_view.xml | 12 +- .../catalog_product_view_type_simple.xml | 2 +- .../catalog_product_view_type_virtual.xml | 2 +- .../layout/checkout_cart_item_renderers.xml | 2 +- .../Catalog/view/frontend/layout/default.xml | 2 +- .../Catalog/view/frontend/requirejs-config.js | 3 +- .../frontend/templates/category/cms.phtml | 2 +- .../templates/category/description.phtml | 2 +- .../frontend/templates/category/image.phtml | 2 +- .../templates/category/products.phtml | 2 +- .../frontend/templates/category/rss.phtml | 2 +- .../category/widget/link/link_block.phtml | 2 +- .../category/widget/link/link_inline.phtml | 2 +- .../frontend/templates/navigation/left.phtml | 2 +- .../templates/product/compare/link.phtml | 2 +- .../templates/product/compare/list.phtml | 2 +- .../templates/product/compare/sidebar.phtml | 2 +- .../frontend/templates/product/gallery.phtml | 2 +- .../frontend/templates/product/image.phtml | 2 +- .../product/image_with_borders.phtml | 2 +- .../frontend/templates/product/list.phtml | 2 +- .../product/list/addto/compare.phtml | 2 +- .../templates/product/list/items.phtml | 4 +- .../templates/product/list/toolbar.phtml | 2 +- .../product/list/toolbar/amount.phtml | 2 +- .../product/list/toolbar/limiter.phtml | 2 +- .../product/list/toolbar/sorter.phtml | 2 +- .../product/list/toolbar/viewmode.phtml | 2 +- .../frontend/templates/product/listing.phtml | 2 +- .../templates/product/view/additional.phtml | 2 +- .../templates/product/view/addto.phtml | 2 +- .../product/view/addto/compare.phtml | 2 +- .../templates/product/view/addtocart.phtml | 8 +- .../templates/product/view/attribute.phtml | 2 +- .../templates/product/view/attributes.phtml | 4 +- .../templates/product/view/description.phtml | 2 +- .../templates/product/view/details.phtml | 2 +- .../templates/product/view/form.phtml | 2 +- .../templates/product/view/gallery.phtml | 2 +- .../templates/product/view/mailto.phtml | 2 +- .../product/view/opengraph/currency.phtml | 2 +- .../product/view/opengraph/general.phtml | 2 +- .../templates/product/view/options.phtml | 2 +- .../product/view/options/type/date.phtml | 2 +- .../product/view/options/type/default.phtml | 2 +- .../product/view/options/type/file.phtml | 2 +- .../product/view/options/type/select.phtml | 2 +- .../product/view/options/type/text.phtml | 2 +- .../product/view/options/wrapper.phtml | 2 +- .../product/view/options/wrapper/bottom.phtml | 2 +- .../templates/product/view/price_clone.phtml | 2 +- .../templates/product/view/review.phtml | 2 +- .../templates/product/view/type/default.phtml | 2 +- .../product/widget/link/link_block.phtml | 2 +- .../product/widget/link/link_inline.phtml | 2 +- .../widget/new/column/new_default_list.phtml | 2 +- .../widget/new/column/new_images_list.phtml | 2 +- .../widget/new/column/new_names_list.phtml | 2 +- .../product/widget/new/content/new_grid.phtml | 2 +- .../product/widget/new/content/new_list.phtml | 2 +- .../frontend/web/js/catalog-add-to-cart.js | 78 +- .../Catalog/view/frontend/web/js/compare.js | 36 - .../Catalog/view/frontend/web/js/gallery.js | 29 +- .../Catalog/view/frontend/web/js/list.js | 32 +- .../frontend/web/js/product/list/toolbar.js | 55 +- .../view/frontend/web/js/related-products.js | 63 +- .../view/frontend/web/js/upsell-products.js | 39 +- .../view/frontend/web/js/validate-product.js | 2 +- .../frontend/web/js/view/compare-products.js | 33 +- .../view/frontend/web/js/view/image.js | 3 +- .../Catalog/view/frontend/web/js/zoom.js | 100 +- .../frontend/web/product/view/validation.js | 27 +- .../frontend/web/template/product/image.html | 2 +- .../template/product/image_with_borders.html | 2 +- .../Model/Export/Product.php | 95 +- .../Export/Product/Type/AbstractType.php | 2 +- .../Model/Export/Product/Type/Factory.php | 2 +- .../Model/Export/Product/Type/Simple.php | 2 +- .../Model/Export/RowCustomizer/Composite.php | 2 +- .../Model/Export/RowCustomizerInterface.php | 2 +- .../Model/Import/Product.php | 316 +- .../Import/Product/CategoryProcessor.php | 2 +- .../Model/Import/Product/Option.php | 2 +- .../Import/Product/RowValidatorInterface.php | 4 +- .../Model/Import/Product/SkuProcessor.php | 2 +- .../Model/Import/Product/StoreResolver.php | 2 +- .../Import/Product/TaxClassProcessor.php | 2 +- .../Import/Product/Type/AbstractType.php | 52 +- .../Model/Import/Product/Type/Factory.php | 2 +- .../Model/Import/Product/Type/Simple.php | 2 +- .../Model/Import/Product/Type/Virtual.php | 2 +- .../Model/Import/Product/Validator.php | 50 +- .../Validator/AbstractImportValidator.php | 2 +- .../Product/Validator/AbstractPrice.php | 2 +- .../Model/Import/Product/Validator/Media.php | 17 +- .../Import/Product/Validator/Quantity.php | 2 +- .../Product/Validator/SuperProductsSku.php | 2 +- .../Import/Product/Validator/TierPrice.php | 2 +- .../Import/Product/Validator/Website.php | 2 +- .../Model/Import/Product/Validator/Weight.php | 2 +- .../Model/Import/Proxy/Product.php | 2 +- .../Import/Proxy/Product/ResourceModel.php | 2 +- .../Model/Import/Uploader.php | 19 +- .../Category/Product/Plugin/Import.php | 2 +- .../Product/Category/Plugin/Import.php | 2 +- .../Indexer/Product/Eav/Plugin/Import.php | 2 +- .../Indexer/Product/Flat/Plugin/Import.php | 2 +- .../Indexer/Product/Price/Plugin/Import.php | 2 +- .../Model/Indexer/Stock/Plugin/Import.php | 2 +- .../Test/Unit/Model/Export/ProductTest.php | 2 +- .../Test/Unit/Model/Export/StubProduct.php | 2 +- .../Import/Product/CategoryProcessorTest.php | 2 +- .../Model/Import/Product/SkuProcessorTest.php | 2 +- .../Import/Product/TaxClassProcessorTest.php | 2 +- .../Import/Product/Type/AbstractTypeTest.php | 2 +- .../Model/Import/Product/Type/OptionTest.php | 2 +- .../Model/Import/Product/Type/VirtualTest.php | 2 +- .../row_data_ambiguity_different_type.php | 2 +- .../row_data_ambiguity_several_db_rows.php | 2 +- .../Type/_files/row_data_main_empty_title.php | 2 +- .../_files/row_data_main_incorrect_type.php | 2 +- .../row_data_main_invalid_max_characters.php | 2 +- .../_files/row_data_main_invalid_price.php | 2 +- .../row_data_main_invalid_sort_order.php | 2 +- .../_files/row_data_main_invalid_store.php | 2 +- ...row_data_main_max_characters_less_zero.php | 2 +- .../Type/_files/row_data_main_no_title.php | 2 +- .../row_data_main_sort_order_less_zero.php | 2 +- .../Type/_files/row_data_main_valid.php | 2 +- .../Type/_files/row_data_no_custom_option.php | 2 +- .../row_data_secondary_incorrect_price.php | 2 +- .../row_data_secondary_incorrect_row_sort.php | 2 +- .../row_data_secondary_invalid_store.php | 2 +- .../row_data_secondary_row_sort_less_zero.php | 2 +- .../Type/_files/row_data_secondary_valid.php | 2 +- .../Import/Product/Validator/MediaTest.php | 31 +- .../Import/Product/Validator/QuantityTest.php | 2 +- .../Product/Validator/TierPriceTest.php | 2 +- .../Model/Import/Product/ValidatorTest.php | 2 +- .../Test/Unit/Model/Import/ProductTest.php | 40 +- .../Test/Unit/Model/Import/UploaderTest.php | 59 +- .../Product/Flat/Plugin/ImportTest.php | 2 +- .../Product/Price/Plugin/ImportTest.php | 2 +- .../Model/Indexer/Stock/Plugin/ImportTest.php | 2 +- .../Magento/CatalogImportExport/composer.json | 2 +- .../CatalogImportExport/etc/config.xml | 2 +- .../Magento/CatalogImportExport/etc/di.xml | 2 +- .../CatalogImportExport/etc/export.xml | 2 +- .../CatalogImportExport/etc/import.xml | 2 +- .../CatalogImportExport/etc/module.xml | 2 +- .../CatalogImportExport/registration.php | 2 +- .../Api/Data/StockCollectionInterface.php | 2 +- .../Api/Data/StockInterface.php | 2 +- .../Api/Data/StockItemCollectionInterface.php | 2 +- .../Api/Data/StockItemInterface.php | 2 +- .../Data/StockStatusCollectionInterface.php | 2 +- .../Api/Data/StockStatusInterface.php | 2 +- .../Api/StockConfigurationInterface.php | 2 +- .../Api/StockCriteriaInterface.php | 2 +- .../Api/StockIndexInterface.php | 2 +- .../Api/StockItemCriteriaInterface.php | 2 +- .../Api/StockItemRepositoryInterface.php | 2 +- .../Api/StockManagementInterface.php | 2 +- .../Api/StockRegistryInterface.php | 2 +- .../Api/StockRepositoryInterface.php | 2 +- .../Api/StockStateInterface.php | 2 +- .../Api/StockStatusCriteriaInterface.php | 2 +- .../Api/StockStatusRepositoryInterface.php | 2 +- .../Adminhtml/Form/Field/Customergroup.php | 2 +- .../Block/Adminhtml/Form/Field/Minsaleqty.php | 2 +- .../Block/Adminhtml/Form/Field/Stock.php | 2 +- .../Block/Plugin/ProductView.php | 4 +- .../CatalogInventory/Block/Qtyincrements.php | 2 +- .../Block/Stockqty/AbstractStockqty.php | 4 +- .../Block/Stockqty/Composite.php | 2 +- .../Block/Stockqty/DefaultStockqty.php | 2 +- .../Block/Stockqty/Type/Grouped.php | 2 +- .../Magento/CatalogInventory/Helper/Data.php | 2 +- .../CatalogInventory/Helper/Minsaleqty.php | 18 +- .../Magento/CatalogInventory/Helper/Stock.php | 7 +- .../Model/AddStockStatusToCollection.php | 2 +- .../Model/Adminhtml/Stock/Item.php | 2 +- .../Model/Config/Backend/AbstractValue.php | 2 +- .../Model/Config/Backend/Backorders.php | 2 +- .../Model/Config/Backend/Managestock.php | 2 +- .../Model/Config/Backend/ShowOutOfStock.php | 2 +- .../CatalogInventory/Model/Configuration.php | 20 +- .../CatalogInventory/Model/Indexer/Stock.php | 2 +- .../Model/Indexer/Stock/AbstractAction.php | 47 +- .../Model/Indexer/Stock/Action/Full.php | 2 +- .../Model/Indexer/Stock/Action/Row.php | 2 +- .../Model/Indexer/Stock/Action/Rows.php | 2 +- .../Model/Indexer/Stock/CacheCleaner.php | 136 + .../Model/Indexer/Stock/Plugin/StoreGroup.php | 2 +- .../Model/Indexer/Stock/Processor.php | 2 +- .../Model/Plugin/AfterProductLoad.php | 2 +- .../Plugin/AroundProductRepositorySave.php | 2 +- .../Model/Plugin/FilterCustomAttribute.php | 2 +- .../CatalogInventory/Model/Plugin/Layer.php | 2 +- .../Model/Plugin/ProductLinks.php | 2 +- .../CopyConstructor/CatalogInventory.php | 2 +- .../Model/Quote/Item/QuantityValidator.php | 35 +- .../QuantityValidator/Initializer/Option.php | 2 +- .../Initializer/QtyProcessor.php | 2 +- .../Initializer/StockItem.php | 2 +- .../QuantityValidator/QuoteItemQtyList.php | 2 +- .../Indexer/Stock/DefaultStock.php | 2 +- .../Indexer/Stock/QueryProcessorComposite.php | 2 +- .../Indexer/Stock/QueryProcessorInterface.php | 2 +- .../Indexer/Stock/StockInterface.php | 2 +- .../ResourceModel/Indexer/StockFactory.php | 2 +- .../StockStatusBaseSelectProcessor.php | 51 + .../ResourceModel/QtyCounterInterface.php | 2 +- .../Model/ResourceModel/Stock.php | 2 +- .../Model/ResourceModel/Stock/Collection.php | 2 +- .../Model/ResourceModel/Stock/Item.php | 2 +- .../ResourceModel/Stock/Item/Collection.php | 2 +- .../Stock/Item/StockItemCriteria.php | 2 +- .../Stock/Item/StockItemCriteriaMapper.php | 2 +- .../Model/ResourceModel/Stock/Status.php | 2 +- .../ResourceModel/Stock/Status/Collection.php | 2 +- .../Stock/Status/StockStatusCriteria.php | 2 +- .../Status/StockStatusCriteriaMapper.php | 2 +- .../ResourceModel/Stock/StockCriteria.php | 2 +- .../Stock/StockCriteriaMapper.php | 2 +- .../Model/Source/Backorders.php | 2 +- .../CatalogInventory/Model/Source/Stock.php | 2 +- .../Model/Source/StockConfiguration.php | 2 +- .../Spi/StockRegistryProviderInterface.php | 2 +- .../Model/Spi/StockStateProviderInterface.php | 2 +- .../Magento/CatalogInventory/Model/Stock.php | 2 +- .../CatalogInventory/Model/Stock/Item.php | 2 +- .../CatalogInventory/Model/Stock/Status.php | 4 +- .../Model/Stock/StockItemRepository.php | 31 +- .../Model/Stock/StockRepository.php | 2 +- .../Model/Stock/StockStatusRepository.php | 2 +- .../CatalogInventory/Model/StockIndex.php | 2 +- .../Model/StockManagement.php | 2 +- .../CatalogInventory/Model/StockRegistry.php | 2 +- .../Model/StockRegistryProvider.php | 2 +- .../Model/StockRegistryStorage.php | 2 +- .../CatalogInventory/Model/StockState.php | 2 +- .../Model/StockStateProvider.php | 2 +- .../Model/System/Config/Backend/Minqty.php | 2 +- .../System/Config/Backend/Minsaleqty.php | 2 +- .../System/Config/Backend/Qtyincrements.php | 2 +- .../Observer/AddInventoryDataObserver.php | 2 +- .../Observer/CancelOrderItemObserver.php | 2 +- .../CheckoutAllSubmitAfterObserver.php | 2 +- .../DisplayProductStatusInfoObserver.php | 2 +- .../Observer/ItemsForReindex.php | 2 +- .../CatalogInventory/Observer/ProductQty.php | 2 +- .../Observer/QuantityValidatorObserver.php | 2 +- .../ReindexQuoteInventoryObserver.php | 2 +- .../Observer/RevertQuoteInventoryObserver.php | 2 +- .../Observer/SaveInventoryDataObserver.php | 2 +- .../SubtractQuoteInventoryObserver.php | 2 +- ...dateItemsStockUponConfigChangeObserver.php | 2 +- .../CatalogInventory/Setup/InstallData.php | 2 +- .../CatalogInventory/Setup/InstallSchema.php | 6 +- .../CatalogInventory/Setup/Recurring.php | 2 +- .../CatalogInventory/Setup/UpgradeData.php | 82 +- .../CatalogInventory/Setup/UpgradeSchema.php | 123 + .../Test/Unit/Api/StockConfigurationTest.php | 2 +- .../Test/Unit/Api/StockRegistryTest.php | 2 +- .../Test/Unit/Api/StockStateTest.php | 2 +- .../Block/Adminhtml/Form/Field/StockTest.php | 2 +- .../Unit/Block/Plugin/ProductViewTest.php | 8 +- .../Test/Unit/Block/QtyincrementsTest.php | 2 +- .../Block/Stockqty/DefaultStockqtyTest.php | 2 +- .../Test/Unit/Helper/MinsaleqtyTest.php | 141 +- .../Test/Unit/Helper/StockTest.php | 2 +- .../Model/AddStockStatusToCollectionTest.php | 2 +- .../Unit/Model/Adminhtml/Stock/ItemTest.php | 4 +- .../Model/Config/Backend/ManagestockTest.php | 2 +- .../Test/Unit/Model/ConfigurationTest.php | 2 +- .../Model/Indexer/Stock/Action/FullTest.php | 2 +- .../Model/Indexer/Stock/Action/RowTest.php | 2 +- .../Model/Indexer/Stock/Action/RowsTest.php | 2 +- .../Model/Indexer/Stock/CacheCleanerTest.php | 169 + .../Indexer/Stock/Plugin/StoreGroupTest.php | 2 +- .../Model/Plugin/AfterProductLoadTest.php | 2 +- .../AroundProductRepositorySaveTest.php | 2 +- .../Test/Unit/Model/Plugin/LayerTest.php | 2 +- .../Unit/Model/Plugin/ProductLinksTest.php | 2 +- .../CopyConstructor/CatalogInventoryTest.php | 2 +- .../Initializer/OptionTest.php | 2 +- .../Initializer/QtyProcessorTest.php | 2 +- .../Initializer/QuantityValidatorTest.php | 84 +- .../Initializer/StockItemTest.php | 2 +- .../StockStatusBaseSelectProcessorTest.php | 69 + .../Model/Spi/StockRegistryProviderTest.php | 2 +- .../Unit/Model/Spi/StockStateProviderTest.php | 2 +- .../Test/Unit/Model/Stock/ItemTest.php | 2 +- .../Model/Stock/StockItemRepositoryTest.php | 29 +- .../Unit/Model/Stock/StockRepositoryTest.php | 2 +- .../Model/Stock/StockStatusRepositoryTest.php | 2 +- .../Test/Unit/Model/StockRegistryTest.php | 2 +- .../Test/Unit/Model/StockTest.php | 2 +- .../Observer/AddInventoryDataObserverTest.php | 2 +- .../CheckoutAllSubmitAfterObserverTest.php | 2 +- ...ItemsStockUponConfigChangeObserverTest.php | 2 +- .../Form/Element/UseConfigSettingsTest.php | 2 +- .../Form/Modifier/AdvancedInventoryTest.php | 2 +- .../Form/Element/UseConfigSettings.php | 2 +- .../Product/AddQuantityFieldToCollection.php | 2 +- .../Product/AddQuantityFilterToCollection.php | 2 +- .../Form/Modifier/AdvancedInventory.php | 2 +- .../Magento/CatalogInventory/composer.json | 2 +- app/code/Magento/CatalogInventory/etc/acl.xml | 2 +- .../CatalogInventory/etc/adminhtml/di.xml | 2 +- .../CatalogInventory/etc/adminhtml/system.xml | 2 +- .../Magento/CatalogInventory/etc/config.xml | 2 +- app/code/Magento/CatalogInventory/etc/di.xml | 9 +- .../Magento/CatalogInventory/etc/events.xml | 5 +- .../etc/extension_attributes.xml | 4 +- .../CatalogInventory/etc/frontend/di.xml | 2 +- .../Magento/CatalogInventory/etc/indexer.xml | 2 +- .../Magento/CatalogInventory/etc/module.xml | 4 +- .../Magento/CatalogInventory/etc/mview.xml | 2 +- .../CatalogInventory/etc/product_types.xml | 2 +- .../Magento/CatalogInventory/etc/webapi.xml | 2 +- .../Magento/CatalogInventory/registration.php | 2 +- .../adminhtml/ui_component/product_form.xml | 3 +- .../ui_component/product_listing.xml | 2 +- .../js/components/qty-validator-changer.js | 2 +- .../js/components/use-config-min-sale-qty.js | 2 +- .../web/js/components/use-config-settings.js | 4 +- .../frontend/layout/catalog_product_view.xml | 2 +- .../catalog_product_view_type_simple.xml | 2 +- .../catalog_product_view_type_virtual.xml | 2 +- .../frontend/templates/qtyincrements.phtml | 2 +- .../templates/stockqty/composite.phtml | 2 +- .../frontend/templates/stockqty/default.phtml | 2 +- .../LICENSE.txt | 48 + .../LICENSE_AFL.txt | 48 + .../GetInStockAttributeOptionsPlugin.php | 74 + .../README.md | 1 + .../GetInStockAttributeOptionsPluginTest.php | 275 + .../composer.json | 24 + .../etc/frontend/di.xml | 12 + .../etc/module.xml | 10 + .../registration.php | 11 + .../Api/CatalogRuleRepositoryInterface.php | 2 +- .../Api/Data/ConditionInterface.php | 2 +- .../CatalogRule/Api/Data/RuleInterface.php | 2 +- .../Block/Adminhtml/Edit/DeleteButton.php | 2 +- .../Block/Adminhtml/Edit/GenericButton.php | 2 +- .../Block/Adminhtml/Edit/ResetButton.php | 2 +- .../Adminhtml/Edit/SaveAndApplyButton.php | 2 +- .../Adminhtml/Edit/SaveAndContinueButton.php | 2 +- .../Block/Adminhtml/Edit/SaveButton.php | 2 +- .../Block/Adminhtml/Promo/Catalog.php | 2 +- .../Promo/Catalog/Edit/Tab/Conditions.php | 2 +- .../Adminhtml/Promo/Widget/Chooser/Sku.php | 2 +- .../Controller/Adminhtml/Promo/Catalog.php | 2 +- .../Adminhtml/Promo/Catalog/ApplyRules.php | 2 +- .../Adminhtml/Promo/Catalog/Chooser.php | 2 +- .../Adminhtml/Promo/Catalog/Delete.php | 2 +- .../Adminhtml/Promo/Catalog/Edit.php | 2 +- .../Adminhtml/Promo/Catalog/Index.php | 2 +- .../Adminhtml/Promo/Catalog/NewAction.php | 2 +- .../Adminhtml/Promo/Catalog/NewActionHtml.php | 2 +- .../Promo/Catalog/NewConditionHtml.php | 2 +- .../Adminhtml/Promo/Catalog/Save.php | 2 +- .../Controller/Adminhtml/Promo/Index.php | 2 +- .../Controller/Adminhtml/Promo/Widget.php | 2 +- .../Adminhtml/Promo/Widget/CategoriesJson.php | 2 +- .../Adminhtml/Promo/Widget/Chooser.php | 2 +- .../Controller/RegistryConstants.php | 2 +- .../CatalogRule/Cron/DailyCatalogUpdate.php | 2 +- app/code/Magento/CatalogRule/Helper/Data.php | 2 +- .../Model/CatalogRuleRepository.php | 2 +- .../CatalogRule/Model/Data/Condition.php | 2 +- .../Model/Data/Condition/Converter.php | 2 +- app/code/Magento/CatalogRule/Model/Flag.php | 2 +- .../Model/Indexer/AbstractIndexer.php | 2 +- .../Model/Indexer/IndexBuilder.php | 2 +- .../Indexer/Product/ProductRuleIndexer.php | 2 +- .../Indexer/Product/ProductRuleProcessor.php | 2 +- .../Model/Indexer/Rule/RuleProductIndexer.php | 2 +- .../Indexer/Rule/RuleProductProcessor.php | 2 +- .../Model/Product/PriceModifier.php | 2 +- .../Model/ResourceModel/Grid/Collection.php | 2 +- .../Product/CollectionProcessor.php | 95 + ...ProductSelectBuilderByCatalogRulePrice.php | 51 +- .../Model/ResourceModel/ReadHandler.php | 2 +- .../CatalogRule/Model/ResourceModel/Rule.php | 2 +- .../Model/ResourceModel/Rule/Collection.php | 18 +- .../ResourceModel/Rule/Product/Price.php | 2 +- .../Rule/Product/Price/Collection.php | 2 +- .../Model/ResourceModel/SaveHandler.php | 2 +- app/code/Magento/CatalogRule/Model/Rule.php | 30 +- .../Model/Rule/Action/Collection.php | 2 +- .../CatalogRule/Model/Rule/Action/Product.php | 2 +- .../Action/SimpleActionOptionsProvider.php | 2 +- .../Model/Rule/Condition/Combine.php | 2 +- .../Model/Rule/Condition/Product.php | 2 +- .../Rule/CustomerGroupsOptionsProvider.php | 2 +- .../CatalogRule/Model/Rule/DataProvider.php | 2 +- .../Magento/CatalogRule/Model/Rule/Job.php | 2 +- .../CatalogRule/Model/Rule/Product/Price.php | 2 +- .../Model/Rule/WebsitesOptionsProvider.php | 2 +- .../Observer/AddDirtyRulesNotice.php | 2 +- ...CatalogProductCollectionPricesObserver.php | 2 +- .../ProcessAdminFinalPriceObserver.php | 2 +- .../ProcessFrontFinalPriceObserver.php | 2 +- .../Observer/RulePricesStorage.php | 2 +- .../CatalogRule/Plugin/Indexer/Category.php | 2 +- .../Plugin/Indexer/CustomerGroup.php | 2 +- .../Plugin/Indexer/ImportExport.php | 2 +- .../Plugin/Indexer/Product/Attribute.php | 2 +- .../Indexer/Product/Save/ApplyRules.php | 2 +- .../Product/Save/ApplyRulesAfterReindex.php | 2 +- .../CatalogRule/Plugin/Indexer/Website.php | 2 +- .../Plugin/Model/Product/Action.php | 2 +- .../Pricing/Price/CatalogRulePrice.php | 8 +- .../Magento/CatalogRule/Setup/InstallData.php | 2 +- .../CatalogRule/Setup/InstallSchema.php | 9 +- .../Magento/CatalogRule/Setup/UpgradeData.php | 82 + .../CatalogRule/Setup/UpgradeSchema.php | 16 +- .../Block/Adminhtml/Edit/DeleteButtonTest.php | 2 +- .../Adminhtml/Edit/GenericButtonTest.php | 2 +- .../Test/Unit/Cron/DailyCatalogUpdateTest.php | 2 +- .../CatalogRule/Test/Unit/Helper/DataTest.php | 2 +- .../Unit/Model/CatalogRuleRepositoryTest.php | 2 +- .../Model/Data/Condition/ConverterTest.php | 2 +- .../Model/Indexer/AbstractIndexerTest.php | 2 +- .../Unit/Model/Indexer/IndexBuilderTest.php | 2 +- .../Product/ProductRuleIndexerTest.php | 2 +- .../Indexer/Rule/RuleProductIndexerTest.php | 2 +- .../Unit/Model/Product/PriceModifierTest.php | 2 +- .../Model/ResourceModel/ReadHandlerTest.php | 2 +- .../Model/ResourceModel/SaveHandlerTest.php | 2 +- .../Unit/Model/Rule/Condition/ProductTest.php | 2 +- .../CustomerGroupsOptionsProviderTest.php | 2 +- .../Test/Unit/Model/Rule/DataProviderTest.php | 2 +- .../Test/Unit/Model/Rule/JobTest.php | 2 +- .../Rule/WebsitesOptionsProviderTest.php | 2 +- .../CatalogRule/Test/Unit/Model/RuleTest.php | 94 +- .../Unit/Observer/AddDirtyRulesNoticeTest.php | 2 +- .../Test/Unit/Plugin/Indexer/CategoryTest.php | 2 +- .../Unit/Plugin/Indexer/CustomerGroupTest.php | 2 +- .../Unit/Plugin/Indexer/ImportExportTest.php | 2 +- .../Save/ApplyRulesAfterReindexTest.php | 2 +- .../Indexer/Product/Save/ApplyRulesTest.php | 2 +- .../Test/Unit/Plugin/Indexer/WebsiteTest.php | 2 +- .../Unit/Plugin/Model/Product/ActionTest.php | 2 +- .../Pricing/Price/CatalogRulePriceTest.php | 14 +- app/code/Magento/CatalogRule/composer.json | 2 +- app/code/Magento/CatalogRule/etc/acl.xml | 2 +- .../Magento/CatalogRule/etc/adminhtml/di.xml | 2 +- .../CatalogRule/etc/adminhtml/events.xml | 2 +- .../CatalogRule/etc/adminhtml/menu.xml | 2 +- .../CatalogRule/etc/adminhtml/routes.xml | 2 +- app/code/Magento/CatalogRule/etc/crontab.xml | 2 +- .../CatalogRule/etc/crontab/events.xml | 2 +- app/code/Magento/CatalogRule/etc/di.xml | 2 +- app/code/Magento/CatalogRule/etc/events.xml | 2 +- .../CatalogRule/etc/frontend/events.xml | 2 +- app/code/Magento/CatalogRule/etc/indexer.xml | 2 +- app/code/Magento/CatalogRule/etc/module.xml | 4 +- app/code/Magento/CatalogRule/etc/mview.xml | 2 +- .../CatalogRule/etc/webapi_rest/di.xml | 2 +- .../CatalogRule/etc/webapi_rest/events.xml | 2 +- .../CatalogRule/etc/webapi_soap/events.xml | 2 +- app/code/Magento/CatalogRule/registration.php | 2 +- .../catalog_rule_promo_catalog_block.xml | 2 +- .../catalog_rule_promo_catalog_edit.xml | 2 +- .../catalog_rule_promo_catalog_index.xml | 2 +- .../adminhtml/templates/promo/fieldset.phtml | 2 +- .../view/adminhtml/templates/promo/form.phtml | 2 +- .../ui_component/catalog_rule_form.xml | 4 +- .../Model/ConfigurableProductsProvider.php | 2 +- .../Model/Indexer/ProductRuleReindex.php | 2 +- .../Model/Rule/ConfigurableProductHandler.php | 2 +- .../CatalogRule/Model/Rule/Validation.php | 2 +- .../ResourceModel/AddCatalogRulePrice.php | 70 +- .../Rule/ConfigurableProductHandlerTest.php | 2 +- .../CatalogRule/Model/Rule/ValidationTest.php | 2 +- .../CatalogRuleConfigurable/composer.json | 4 +- .../etc/adminhtml/di.xml | 2 +- .../etc/crontab/di.xml | 2 +- .../CatalogRuleConfigurable/etc/di.xml | 2 +- .../CatalogRuleConfigurable/etc/module.xml | 2 +- .../CatalogRuleConfigurable/registration.php | 2 +- .../CatalogSearch/Block/Advanced/Form.php | 2 +- .../CatalogSearch/Block/Advanced/Result.php | 13 +- .../Block/Plugin/FrontTabPlugin.php | 2 +- .../Magento/CatalogSearch/Block/Result.php | 2 +- .../Controller/Advanced/Index.php | 9 +- .../Controller/Advanced/Result.php | 2 +- .../CatalogSearch/Controller/Result/Index.php | 2 +- .../Magento/CatalogSearch/Helper/Data.php | 2 +- .../Aggregation/AggregationResolver.php | 2 +- .../Mysql/Aggregation/DataProvider.php | 34 +- .../Adapter/Mysql/Dynamic/DataProvider.php | 2 +- .../Model/Adapter/Mysql/Field/Resolver.php | 2 +- .../Adapter/Mysql/Filter/AliasResolver.php | 44 + .../Adapter/Mysql/Filter/Preprocessor.php | 59 +- .../Aggregation/Category/DataProvider.php | 2 +- .../CatalogSearch/Model/Adapter/Options.php | 2 +- .../System/Config/Backend/Engine.php | 2 +- .../Magento/CatalogSearch/Model/Advanced.php | 10 +- .../Model/Advanced/Request/Builder.php | 2 +- .../Model/Attribute/SearchWeight.php | 63 + .../Model/Autocomplete/DataProvider.php | 2 +- .../Magento/CatalogSearch/Model/Fulltext.php | 2 +- .../CatalogSearch/Model/Indexer/Fulltext.php | 58 +- .../Indexer/Fulltext/Action/DataProvider.php | 2 +- .../Model/Indexer/Fulltext/Action/Full.php | 2 +- .../Indexer/Fulltext/Action/IndexIterator.php | 2 +- .../Fulltext/Plugin/AbstractPlugin.php | 2 +- .../Indexer/Fulltext/Plugin/Attribute.php | 4 +- .../Indexer/Fulltext/Plugin/Category.php | 2 +- .../Model/Indexer/Fulltext/Plugin/Product.php | 2 +- .../Fulltext/Plugin/Product/Action.php | 2 +- .../Indexer/Fulltext/Plugin/Store/Group.php | 2 +- .../Indexer/Fulltext/Plugin/Store/View.php | 2 +- .../Model/Indexer/Fulltext/Processor.php | 2 +- .../Model/Indexer/Fulltext/Store.php | 2 +- .../Model/Indexer/IndexStructure.php | 8 +- .../Model/Indexer/IndexStructureFactory.php | 2 +- .../Model/Indexer/IndexStructureProxy.php | 2 +- .../Model/Indexer/IndexSwitcherInterface.php | 22 + .../Model/Indexer/IndexSwitcherProxy.php | 100 + .../Model/Indexer/IndexerHandler.php | 8 +- .../Model/Indexer/IndexerHandlerFactory.php | 8 +- .../Model/Indexer/Mview/Action.php | 2 +- .../Model/Indexer/ProductFieldset.php | 2 +- .../Model/Indexer/Scope/IndexSwitcher.php | 76 + .../Scope/IndexTableNotExistException.php | 18 + .../Model/Indexer/Scope/ScopeProxy.php | 76 + .../Model/Indexer/Scope/State.php | 65 + .../Model/Indexer/Scope/TemporaryResolver.php | 43 + .../Indexer/Scope/UnknownStateException.php | 18 + .../Layer/Category/ItemCollectionProvider.php | 2 +- .../Model/Layer/Filter/Attribute.php | 2 +- .../Model/Layer/Filter/Category.php | 2 +- .../Model/Layer/Filter/Decimal.php | 4 +- .../Model/Layer/Filter/Price.php | 2 +- .../Layer/Search/Plugin/CollectionFilter.php | 2 +- .../Model/Layer/Search/StateKey.php | 2 +- .../CatalogSearch/Model/Price/Interval.php | 2 +- .../Model/ResourceModel/Advanced.php | 2 +- .../ResourceModel/Advanced/Collection.php | 24 +- .../Model/ResourceModel/Engine.php | 2 +- .../Model/ResourceModel/EngineInterface.php | 2 +- .../Model/ResourceModel/EngineProvider.php | 2 +- .../Model/ResourceModel/Fulltext.php | 2 +- .../ResourceModel/Fulltext/Collection.php | 21 +- .../Model/ResourceModel/Search/Collection.php | 2 +- .../CatalogSearch/Model/Search/Catalog.php | 2 +- .../Search/FilterMapper/ExclusionStrategy.php | 83 + .../Search/FilterMapper/FilterContext.php | 100 + .../FilterMapper/FilterStrategyInterface.php | 23 + .../FilterMapper/StaticAttributeStrategy.php | 74 + .../FilterMapper/TermDropdownStrategy.php | 127 + .../Model/Search/IndexBuilder.php | 5 +- .../Model/Search/ReaderPlugin.php | 2 +- .../Model/Search/RequestGenerator.php | 67 +- .../Model/Search/RequestGenerator/Decimal.php | 44 + .../Model/Search/RequestGenerator/General.php | 42 + .../RequestGenerator/GeneratorInterface.php | 29 + .../RequestGenerator/GeneratorResolver.php | 46 + .../Model/Search/TableMapper.php | 200 +- .../CatalogSearch/Model/Source/Weight.php | 2 +- .../CatalogSearch/Setup/InstallData.php | 2 +- .../CatalogSearch/Setup/InstallSchema.php | 2 +- .../Unit/Block/Plugin/FrontTabPluginTest.php | 2 +- .../Test/Unit/Block/ResultTest.php | 2 +- .../Unit/Controller/Advanced/ResultTest.php | 2 +- .../Aggregation/AggregationResolverTest.php | 2 +- .../Mysql/Filter/AliasResolverTest.php | 68 + .../Adapter/Mysql/Filter/PreprocessorTest.php | 15 +- .../Test/Unit/Model/Adapter/OptionsTest.php | 2 +- .../Model/Advanced/Request/BuilderTest.php | 2 +- .../Test/Unit/Model/AdvancedTest.php | 4 +- .../Unit/Model/Attribute/SearchWeightTest.php | 101 + .../Model/Autocomplete/DataProviderTest.php | 2 +- .../Indexer/Fulltext/Action/FullTest.php | 2 +- .../Indexer/Fulltext/Plugin/AttributeTest.php | 2 +- .../Indexer/Fulltext/Plugin/CategoryTest.php | 2 +- .../Fulltext/Plugin/Product/ActionTest.php | 2 +- .../Indexer/Fulltext/Plugin/ProductTest.php | 2 +- .../Fulltext/Plugin/Store/GroupTest.php | 2 +- .../Fulltext/Plugin/Store/ViewTest.php | 2 +- .../Test/Unit/Model/Indexer/FulltextTest.php | 91 +- .../Indexer/IndexerHandlerFactoryTest.php | 173 + .../Model/Indexer/Scope/IndexSwitcherTest.php | 212 + .../Catalog/ItemCollectionProviderTest.php | 2 +- .../Unit/Model/Layer/Filter/AttributeTest.php | 2 +- .../Unit/Model/Layer/Filter/CategoryTest.php | 2 +- .../Unit/Model/Layer/Filter/DecimalTest.php | 2 +- .../Unit/Model/Layer/Filter/PriceTest.php | 2 +- .../Search/Plugin/CollectionFilterTest.php | 2 +- .../ResourceModel/Advanced/CollectionTest.php | 45 +- .../Unit/Model/ResourceModel/AdvancedTest.php | 2 +- .../ResourceModel/BaseCollectionTest.php | 2 +- .../Unit/Model/ResourceModel/EngineTest.php | 2 +- .../ResourceModel/Fulltext/CollectionTest.php | 54 +- .../Unit/Model/ResourceModel/FulltextTest.php | 2 +- .../Search/FilterMapper/FilterContextTest.php | 236 + .../Unit/Model/Search/IndexBuilderTest.php | 2 +- .../Search/Indexer/IndexStructureTest.php | 10 +- .../Unit/Model/Search/ReaderPluginTest.php | 2 +- .../Search/RequestGenerator/DecimalTest.php | 67 + .../Search/RequestGenerator/GeneralTest.php | 65 + .../GeneratorResolverTest.php | 76 + .../Model/Search/RequestGeneratorTest.php | 53 +- .../Unit/Model/Search/TableMapperTest.php | 302 +- app/code/Magento/CatalogSearch/composer.json | 2 +- .../CatalogSearch/etc/adminhtml/di.xml | 2 +- .../CatalogSearch/etc/adminhtml/system.xml | 2 +- .../CatalogSearch/etc/catalog_attributes.xml | 2 +- app/code/Magento/CatalogSearch/etc/config.xml | 2 +- app/code/Magento/CatalogSearch/etc/di.xml | 44 +- app/code/Magento/CatalogSearch/etc/events.xml | 2 +- .../Magento/CatalogSearch/etc/frontend/di.xml | 2 +- .../CatalogSearch/etc/frontend/page_types.xml | 2 +- .../CatalogSearch/etc/frontend/routes.xml | 4 +- .../Magento/CatalogSearch/etc/indexer.xml | 2 +- app/code/Magento/CatalogSearch/etc/module.xml | 2 +- app/code/Magento/CatalogSearch/etc/mview.xml | 2 +- .../CatalogSearch/etc/search_request.xml | 2 +- .../Magento/CatalogSearch/registration.php | 2 +- .../product_attribute_add_form.xml | 2 +- .../layout/catalogsearch_advanced_index.xml | 2 +- .../layout/catalogsearch_advanced_result.xml | 2 +- .../layout/catalogsearch_result_index.xml | 2 +- .../view/frontend/layout/default.xml | 2 +- .../view/frontend/requirejs-config.js | 4 +- .../frontend/templates/advanced/form.phtml | 2 +- .../frontend/templates/advanced/link.phtml | 2 +- .../frontend/templates/advanced/result.phtml | 2 +- .../view/frontend/templates/result.phtml | 2 +- .../Block/UrlKeyRenderer.php | 2 +- .../Category/CanonicalUrlRewriteGenerator.php | 2 +- .../Category/ChildrenCategoriesProvider.php | 2 +- .../Category/ChildrenUrlRewriteGenerator.php | 33 +- .../CurrentUrlRewritesRegenerator.php | 116 +- .../Model/Category/Plugin/Category/Move.php | 2 +- .../Model/Category/Plugin/Category/Remove.php | 45 +- .../Model/Category/Plugin/Storage.php | 27 +- .../Model/Category/Plugin/Store/Group.php | 4 +- .../Model/Category/Plugin/Store/View.php | 60 +- .../Model/Category/Product.php | 2 +- .../CategoryBasedProductRewriteGenerator.php | 61 + .../Model/CategoryUrlPathGenerator.php | 2 +- .../Model/CategoryUrlRewriteGenerator.php | 100 +- .../Model/Map/DataCategoryHashMap.php | 89 + .../Map/DataCategoryUrlRewriteDatabaseMap.php | 135 + .../Map/DataCategoryUsedInProductsHashMap.php | 98 + .../Model/Map/DataProductHashMap.php | 95 + .../Map/DataProductUrlRewriteDatabaseMap.php | 124 + .../Model/Map/DatabaseMapInterface.php | 40 + .../Model/Map/DatabaseMapPool.php | 78 + .../Model/Map/HashMapInterface.php | 45 + .../Model/Map/HashMapPool.php | 79 + .../Model/Map/UrlRewriteFinder.php | 125 + .../Model/ObjectRegistry.php | 2 +- .../Product/AnchorUrlRewriteGenerator.php | 2 +- .../Product/CanonicalUrlRewriteGenerator.php | 2 +- .../Product/CategoriesUrlRewriteGenerator.php | 2 +- .../Product/CurrentUrlRewritesRegenerator.php | 144 +- .../Model/ProductScopeRewriteGenerator.php | 193 + .../Model/ProductUrlPathGenerator.php | 2 +- .../Model/ProductUrlRewriteGenerator.php | 143 +- .../Model/ResourceModel/Category/Product.php | 37 +- .../Category/ProductCollection.php | 2 +- .../Model/Storage/DbStorage.php | 2 +- .../Model/UrlRewriteBunchReplacer.php | 38 + .../Observer/AfterImportDataObserver.php | 38 +- ...ategoryProcessUrlRewriteMovingObserver.php | 26 +- ...ategoryProcessUrlRewriteSavingObserver.php | 67 +- ...egorySaveRewritesHistorySetterObserver.php | 2 +- .../CategoryUrlPathAutogeneratorObserver.php | 12 +- .../Observer/ClearProductUrlsObserver.php | 2 +- ...oductProcessUrlRewriteRemovingObserver.php | 2 +- ...ProductProcessUrlRewriteSavingObserver.php | 2 +- .../ProductToWebsiteChangeObserver.php | 2 +- .../ProductUrlKeyAutogeneratorObserver.php | 2 +- .../Observer/UrlRewriteHandler.php | 141 +- .../Adminhtml/Category/Tab/Attributes.php | 2 +- .../Adminhtml/Product/Edit/Tab/Attributes.php | 2 +- .../Product/Initialization/Helper.php | 2 +- .../Service/V1/StoreViewService.php | 2 +- .../CatalogUrlRewrite/Setup/InstallData.php | 2 +- .../CatalogUrlRewrite/Setup/InstallSchema.php | 2 +- .../CatalogUrlRewrite/Setup/Recurring.php | 2 +- .../CanonicalUrlRewriteGeneratorTest.php | 2 +- .../ChildrenCategoriesProviderTest.php | 2 +- .../ChildrenUrlRewriteGeneratorTest.php | 57 +- .../CurrentUrlRewritesRegeneratorTest.php | 110 +- .../Category/Plugin/Category/MoveTest.php | 2 +- .../Category/Plugin/Category/RemoveTest.php | 12 +- .../Model/Category/Plugin/StorageTest.php | 33 +- .../Model/Category/Plugin/Store/GroupTest.php | 2 +- .../Model/Category/Plugin/Store/ViewTest.php | 13 +- ...tegoryBasedProductRewriteGeneratorTest.php | 98 + .../Model/CategoryUrlPathGeneratorTest.php | 5 +- .../Model/CategoryUrlRewriteGeneratorTest.php | 120 +- .../Model/Map/DataCategoryHashMapTest.php | 104 + .../DataCategoryUrlRewriteDatabaseMapTest.php | 125 + .../DataCategoryUsedInProductsHashMapTest.php | 108 + .../Unit/Model/Map/DataProductHashMapTest.php | 121 + .../DataProductUrlRewriteDatabaseMapTest.php | 112 + .../Test/Unit/Model/Map/HashMapPoolTest.php | 89 + .../Unit/Model/Map/UrlRewriteFinderTest.php | 168 + .../Test/Unit/Model/ObjectRegistryTest.php | 2 +- .../CanonicalUrlRewriteGeneratorTest.php | 2 +- .../CategoriesUrlRewriteGeneratorTest.php | 2 +- .../CurrentUrlRewritesRegeneratorTest.php | 109 +- .../ProductScopeRewriteGeneratorTest.php | 214 + .../Model/ProductUrlPathGeneratorTest.php | 2 +- .../Model/ProductUrlRewriteGeneratorTest.php | 205 +- .../Model/UrlRewriteBunchReplacerTest.php | 39 + .../categoryUrlRewritesDataProvider.php | 2 +- .../Observer/AfterImportDataObserverTest.php | 57 +- ...tegoryUrlPathAutogeneratorObserverTest.php | 45 +- .../Observer/ClearProductUrlsObserverTest.php | 2 +- ...uctProcessUrlRewriteSavingObserverTest.php | 2 +- .../Unit/Observer/UrlRewriteHandlerTest.php | 123 + .../Unit/Service/V1/StoreViewServiceTest.php | 2 +- .../Form/Modifier/ProductUrlRewriteTest.php | 2 +- .../Form/Modifier/ProductUrlRewrite.php | 2 +- .../Magento/CatalogUrlRewrite/composer.json | 2 +- .../CatalogUrlRewrite/etc/adminhtml/di.xml | 2 +- .../etc/adminhtml/events.xml | 2 +- .../etc/adminhtml/system.xml | 2 +- .../etc/catalog_attributes.xml | 2 +- app/code/Magento/CatalogUrlRewrite/etc/di.xml | 10 +- .../CatalogUrlRewrite/etc/eav_attributes.xml | 2 +- .../Magento/CatalogUrlRewrite/etc/events.xml | 2 +- .../Magento/CatalogUrlRewrite/etc/module.xml | 2 +- .../CatalogUrlRewrite/registration.php | 2 +- .../adminhtml/ui_component/category_form.xml | 2 +- .../Block/Product/ProductsList.php | 2 +- .../Block/Product/Widget/Conditions.php | 5 +- .../Controller/Adminhtml/Product/Widget.php | 2 +- .../Adminhtml/Product/Widget/Conditions.php | 2 +- app/code/Magento/CatalogWidget/Model/Rule.php | 20 +- .../Model/Rule/Condition/Combine.php | 4 +- .../Model/Rule/Condition/Product.php | 10 +- .../Unit/Block/Product/ProductsListTest.php | 14 +- .../Block/Product/Widget/ConditionsTest.php | 115 +- .../Product/Widget/ConditionsTest.php | 2 +- .../Unit/Model/Rule/Condition/CombineTest.php | 6 +- .../Test/Unit/Model/RuleTest.php | 42 +- app/code/Magento/CatalogWidget/composer.json | 2 +- .../CatalogWidget/etc/adminhtml/routes.xml | 2 +- app/code/Magento/CatalogWidget/etc/di.xml | 2 +- app/code/Magento/CatalogWidget/etc/module.xml | 2 +- app/code/Magento/CatalogWidget/etc/widget.xml | 2 +- .../Magento/CatalogWidget/registration.php | 2 +- .../templates/product/widget/conditions.phtml | 2 +- .../product/widget/content/grid.phtml | 35 +- .../Api/AgreementsValidatorInterface.php | 2 +- .../Api/Data/PaymentDetailsInterface.php | 2 +- .../Api/Data/ShippingInformationInterface.php | 2 +- .../Api/Data/TotalsInformationInterface.php | 2 +- ...tPaymentInformationManagementInterface.php | 2 +- ...ShippingInformationManagementInterface.php | 2 +- ...stTotalsInformationManagementInterface.php | 2 +- .../PaymentInformationManagementInterface.php | 2 +- ...ShippingInformationManagementInterface.php | 2 +- .../TotalsInformationManagementInterface.php | 2 +- .../Checkout/Block/Adminhtml/CartTab.php | 2 +- app/code/Magento/Checkout/Block/Cart.php | 12 +- .../Checkout/Block/Cart/AbstractCart.php | 2 +- .../Checkout/Block/Cart/Additional/Info.php | 2 +- .../Block/Cart/CartTotalsProcessor.php | 2 +- .../Magento/Checkout/Block/Cart/Coupon.php | 2 +- .../Magento/Checkout/Block/Cart/Crosssell.php | 2 +- app/code/Magento/Checkout/Block/Cart/Grid.php | 169 + .../Checkout/Block/Cart/Item/Configure.php | 2 +- .../Checkout/Block/Cart/Item/Renderer.php | 2 +- .../Block/Cart/Item/Renderer/Actions.php | 2 +- .../Block/Cart/Item/Renderer/Actions/Edit.php | 2 +- .../Cart/Item/Renderer/Actions/Generic.php | 2 +- .../Cart/Item/Renderer/Actions/Remove.php | 2 +- .../Checkout/Block/Cart/LayoutProcessor.php | 13 +- app/code/Magento/Checkout/Block/Cart/Link.php | 2 +- .../Magento/Checkout/Block/Cart/Shipping.php | 25 +- .../Magento/Checkout/Block/Cart/Sidebar.php | 40 +- .../Magento/Checkout/Block/Cart/Totals.php | 2 +- .../Block/Cart/ValidationMessages.php | 2 +- .../Block/Checkout/AttributeMerger.php | 19 +- .../Block/Checkout/DirectoryDataProcessor.php | 146 + .../Block/Checkout/LayoutProcessor.php | 296 +- .../Checkout/LayoutProcessorInterface.php | 2 +- .../Block/Checkout/TotalsProcessor.php | 2 +- .../Checkout/Block/Item/Price/Renderer.php | 2 +- app/code/Magento/Checkout/Block/Link.php | 2 +- app/code/Magento/Checkout/Block/Onepage.php | 24 +- .../Checkout/Block/Onepage/Failure.php | 2 +- .../Magento/Checkout/Block/Onepage/Link.php | 2 +- .../Checkout/Block/Onepage/Success.php | 10 +- .../Checkout/Block/QuoteShortcutButtons.php | 2 +- .../Magento/Checkout/Block/Registration.php | 2 +- .../Magento/Checkout/Block/Shipping/Price.php | 2 +- app/code/Magento/Checkout/Block/Success.php | 2 +- .../Checkout/Block/Total/DefaultTotal.php | 2 +- .../Checkout/Controller/Account/Create.php | 2 +- .../Magento/Checkout/Controller/Action.php | 2 +- app/code/Magento/Checkout/Controller/Cart.php | 2 +- .../Magento/Checkout/Controller/Cart/Add.php | 3 +- .../Checkout/Controller/Cart/Addgroup.php | 2 +- .../Checkout/Controller/Cart/Configure.php | 2 +- .../Checkout/Controller/Cart/CouponPost.php | 33 +- .../Checkout/Controller/Cart/Delete.php | 2 +- .../Checkout/Controller/Cart/EstimatePost.php | 2 +- .../Controller/Cart/EstimateUpdatePost.php | 2 +- .../Checkout/Controller/Cart/Index.php | 2 +- .../Controller/Cart/UpdateItemOptions.php | 2 +- .../Checkout/Controller/Cart/UpdatePost.php | 2 +- .../Express/RedirectLoginInterface.php | 2 +- .../Checkout/Controller/Index/Index.php | 2 +- .../Checkout/Controller/Noroute/Index.php | 2 +- .../Magento/Checkout/Controller/Onepage.php | 2 +- .../Checkout/Controller/Onepage/Failure.php | 2 +- .../Checkout/Controller/Onepage/SaveOrder.php | 2 +- .../Checkout/Controller/Onepage/Success.php | 2 +- .../Controller/ShippingRates/Index.php | 2 +- .../Controller/Sidebar/RemoveItem.php | 2 +- .../Controller/Sidebar/UpdateItemQty.php | 2 +- .../Checkout/CustomerData/AbstractItem.php | 2 +- .../Magento/Checkout/CustomerData/Cart.php | 2 +- .../Checkout/CustomerData/DefaultItem.php | 2 +- .../Checkout/CustomerData/DirectoryData.php | 2 +- .../Checkout/CustomerData/ItemInterface.php | 2 +- .../Checkout/CustomerData/ItemPool.php | 2 +- .../CustomerData/ItemPoolInterface.php | 2 +- app/code/Magento/Checkout/Exception.php | 2 +- app/code/Magento/Checkout/Helper/Cart.php | 2 +- app/code/Magento/Checkout/Helper/Data.php | 16 +- .../Checkout/Helper/ExpressRedirect.php | 2 +- .../BillingAddressDisplayOptions.php | 39 + .../Checkout/Model/AgreementsValidator.php | 2 +- app/code/Magento/Checkout/Model/Cart.php | 35 +- .../Checkout/Model/Cart/CartInterface.php | 2 +- .../Cart/CheckoutSummaryConfigProvider.php | 65 + .../Checkout/Model/Cart/CollectQuote.php | 2 +- .../Checkout/Model/Cart/ImageProvider.php | 2 +- .../Checkout/Model/Cart/RequestInfoFilter.php | 44 + .../Model/Cart/RequestInfoFilterComposite.php | 41 + .../Model/Cart/RequestInfoFilterInterface.php | 21 + .../Model/CompositeConfigProvider.php | 2 +- .../Model/Config/Source/Cart/Summary.php | 2 +- .../Model/ConfigProviderInterface.php | 2 +- .../Checkout/Model/DefaultConfigProvider.php | 3 +- .../GuestPaymentInformationManagement.php | 30 +- .../GuestShippingInformationManagement.php | 2 +- .../GuestTotalsInformationManagement.php | 2 +- .../Model/Layout/AbstractTotalsProcessor.php | 2 +- .../Model/Layout/DepersonalizePlugin.php | 2 +- .../Magento/Checkout/Model/PaymentDetails.php | 2 +- .../Model/PaymentInformationManagement.php | 65 +- .../Checkout/Model/ResourceModel/Cart.php | 2 +- app/code/Magento/Checkout/Model/Session.php | 2 +- .../Model/Session/SuccessValidator.php | 2 +- .../Checkout/Model/ShippingInformation.php | 2 +- .../Model/ShippingInformationManagement.php | 3 +- app/code/Magento/Checkout/Model/Sidebar.php | 2 +- .../Checkout/Model/TotalsInformation.php | 2 +- .../Model/TotalsInformationManagement.php | 2 +- .../Magento/Checkout/Model/Type/Onepage.php | 2 +- .../Observer/LoadCustomerQuoteObserver.php | 2 +- .../Observer/SalesQuoteSaveAfterObserver.php | 2 +- .../Checkout/Observer/UnsetAllObserver.php | 2 +- .../Magento/Checkout/Setup/InstallData.php | 2 +- .../Test/Unit/Block/Cart/AbstractCartTest.php | 2 +- .../Block/Cart/CartTotalsProcessorTest.php | 2 +- .../Test/Unit/Block/Cart/GridTest.php | 215 + .../Cart/Item/Renderer/Actions/EditTest.php | 2 +- .../Item/Renderer/Actions/GenericTest.php | 2 +- .../Cart/Item/Renderer/Actions/RemoveTest.php | 2 +- .../Block/Cart/Item/Renderer/ActionsTest.php | 9 +- .../Unit/Block/Cart/Item/RendererTest.php | 2 +- .../Unit/Block/Cart/LayoutProcessorTest.php | 11 +- .../Test/Unit/Block/Cart/LinkTest.php | 2 +- .../Test/Unit/Block/Cart/ShippingTest.php | 29 +- .../Test/Unit/Block/Cart/SidebarTest.php | 24 +- .../Checkout/DirectoryDataProcessorTest.php | 114 + .../Block/Checkout/LayoutProcessorTest.php | 286 + .../Block/Checkout/TotalsProcessorTest.php | 2 +- .../Unit/Block/Item/Price/RendererTest.php | 2 +- .../Checkout/Test/Unit/Block/LinkTest.php | 2 +- .../Test/Unit/Block/Onepage/SuccessTest.php | 67 +- .../Checkout/Test/Unit/Block/OnepageTest.php | 27 +- .../Test/Unit/Block/Shipping/PriceTest.php | 2 +- .../Unit/Controller/Account/CreateTest.php | 2 +- .../Unit/Controller/Cart/ConfigureTest.php | 2 +- .../Unit/Controller/Cart/CouponPostTest.php | 18 +- .../Test/Unit/Controller/Cart/IndexTest.php | 2 +- .../Test/Unit/Controller/Index/IndexTest.php | 2 +- .../Test/Unit/Controller/OnepageTest.php | 2 +- .../Controller/Sidebar/RemoveItemTest.php | 2 +- .../Controller/Sidebar/UpdateItemQtyTest.php | 2 +- .../Test/Unit/Controller/Stub/OnepageStub.php | 2 +- .../Test/Unit/CustomerData/CartTest.php | 2 +- .../Unit/CustomerData/DefaultItemTest.php | 2 +- .../Test/Unit/CustomerData/ItemPoolTest.php | 2 +- .../Checkout/Test/Unit/Helper/CartTest.php | 2 +- .../Checkout/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Helper/ExpressRedirectTest.php | 2 +- .../Unit/Model/AgreementsValidatorTest.php | 2 +- .../CheckoutSummaryConfigProviderTest.php | 55 + .../Unit/Model/Cart/ImageProviderTest.php | 2 +- .../Cart/RequestInfoFilterCompositeTest.php | 73 + .../Unit/Model/Cart/RequestInfoFilterTest.php | 52 + .../Checkout/Test/Unit/Model/CartTest.php | 218 +- .../Model/CompositeConfigProviderTest.php | 2 +- .../Model/Config/Source/Cart/SummaryTest.php | 2 +- .../GuestPaymentInformationManagementTest.php | 36 +- ...GuestShippingInformationManagementTest.php | 2 +- .../Model/Layout/DepersonalizePluginTest.php | 2 +- .../PaymentInformationManagementTest.php | 86 +- .../Model/Session/SuccessValidatorTest.php | 2 +- .../Checkout/Test/Unit/Model/SessionTest.php | 2 +- .../ShippingInformationManagementTest.php | 581 +- .../Checkout/Test/Unit/Model/SidebarTest.php | 2 +- .../Test/Unit/Model/Type/OnepageTest.php | 2 +- .../LoadCustomerQuoteObserverTest.php | 2 +- .../SalesQuoteSaveAfterObserverTest.php | 2 +- .../Unit/Observer/UnsetAllObserverTest.php | 2 +- app/code/Magento/Checkout/composer.json | 2 +- .../Magento/Checkout/etc/adminhtml/system.xml | 17 +- app/code/Magento/Checkout/etc/config.xml | 5 +- app/code/Magento/Checkout/etc/di.xml | 25 +- .../Magento/Checkout/etc/email_templates.xml | 2 +- app/code/Magento/Checkout/etc/events.xml | 2 +- app/code/Magento/Checkout/etc/fieldset.xml | 2 +- app/code/Magento/Checkout/etc/frontend/di.xml | 12 +- .../Magento/Checkout/etc/frontend/events.xml | 2 +- .../Checkout/etc/frontend/page_types.xml | 2 +- .../Magento/Checkout/etc/frontend/routes.xml | 4 +- .../Checkout/etc/frontend/sections.xml | 2 +- app/code/Magento/Checkout/etc/module.xml | 2 +- app/code/Magento/Checkout/etc/webapi.xml | 2 +- app/code/Magento/Checkout/i18n/en_US.csv | 2 +- app/code/Magento/Checkout/registration.php | 2 +- .../view/adminhtml/email/failed_payment.html | 2 +- .../frontend/layout/catalog_category_view.xml | 2 +- .../frontend/layout/catalog_product_view.xml | 2 +- .../layout/checkout_cart_configure.xml | 5 +- .../checkout_cart_configure_type_simple.xml | 2 +- .../frontend/layout/checkout_cart_index.xml | 4 +- .../layout/checkout_cart_item_renderers.xml | 2 +- ...kout_cart_sidebar_item_price_renderers.xml | 2 +- .../checkout_cart_sidebar_item_renderers.xml | 2 +- .../checkout_cart_sidebar_total_renderers.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../layout/checkout_item_price_renderers.xml | 2 +- .../layout/checkout_onepage_failure.xml | 2 +- ...checkout_onepage_review_item_renderers.xml | 2 +- .../layout/checkout_onepage_success.xml | 2 +- .../Checkout/view/frontend/layout/default.xml | 2 +- .../view/frontend/page_layout/checkout.xml | 2 +- .../view/frontend/requirejs-config.js | 2 +- .../view/frontend/templates/button.phtml | 2 +- .../view/frontend/templates/cart.phtml | 2 +- .../templates/cart/additional/info.phtml | 2 +- .../view/frontend/templates/cart/coupon.phtml | 4 +- .../view/frontend/templates/cart/form.phtml | 10 +- .../cart/item/configure/updatecart.phtml | 10 +- .../templates/cart/item/default.phtml | 3 +- .../templates/cart/item/price/sidebar.phtml | 2 +- .../cart/item/renderer/actions/edit.phtml | 2 +- .../cart/item/renderer/actions/remove.phtml | 2 +- .../frontend/templates/cart/methods.phtml | 2 +- .../frontend/templates/cart/minicart.phtml | 4 +- .../frontend/templates/cart/noItems.phtml | 2 +- .../frontend/templates/cart/shipping.phtml | 4 +- .../view/frontend/templates/cart/totals.phtml | 2 +- .../frontend/templates/item/price/row.phtml | 2 +- .../frontend/templates/item/price/unit.phtml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../view/frontend/templates/onepage.phtml | 4 +- .../frontend/templates/onepage/failure.phtml | 2 +- .../frontend/templates/onepage/link.phtml | 2 +- .../templates/onepage/review/item.phtml | 2 +- .../review/item/price/row_excl_tax.phtml | 2 +- .../review/item/price/row_incl_tax.phtml | 2 +- .../review/item/price/unit_excl_tax.phtml | 2 +- .../review/item/price/unit_incl_tax.phtml | 2 +- .../frontend/templates/registration.phtml | 2 +- .../frontend/templates/shipping/price.phtml | 2 +- .../view/frontend/templates/success.phtml | 4 +- .../frontend/templates/total/default.phtml | 2 +- .../web/js/action/create-billing-address.js | 23 +- .../web/js/action/create-shipping-address.js | 46 +- .../web/js/action/get-payment-information.js | 83 +- .../view/frontend/web/js/action/get-totals.js | 85 +- .../frontend/web/js/action/place-order.js | 56 +- .../web/js/action/redirect-on-success.js | 2 +- .../web/js/action/select-billing-address.js | 41 +- .../web/js/action/select-payment-method.js | 24 +- .../web/js/action/select-shipping-address.js | 24 +- .../web/js/action/select-shipping-method.js | 24 +- .../web/js/action/set-billing-address.js | 18 +- .../web/js/action/set-payment-information.js | 102 +- .../web/js/action/set-shipping-information.js | 26 +- .../view/frontend/web/js/checkout-data.js | 124 +- .../view/frontend/web/js/checkout-loader.js | 2 +- .../view/frontend/web/js/discount-codes.js | 18 +- .../web/js/model/address-converter.js | 264 +- .../web/js/model/authentication-messages.js | 4 +- .../view/frontend/web/js/model/cart/cache.js | 191 + .../web/js/model/cart/estimate-service.js | 104 +- .../js/model/cart/totals-processor/default.js | 133 +- .../web/js/model/checkout-data-resolver.js | 402 +- .../web/js/model/customer-email-validator.js | 53 +- .../frontend/web/js/model/error-processor.js | 45 +- .../web/js/model/full-screen-loader.js | 48 +- .../web/js/model/new-customer-address.js | 73 +- .../frontend/web/js/model/payment-service.js | 128 +- .../js/model/payment/additional-validators.js | 92 +- .../web/js/model/payment/method-converter.js | 34 +- .../web/js/model/payment/method-group.js | 31 + .../web/js/model/payment/method-list.js | 19 +- .../web/js/model/payment/renderer-list.js | 20 +- .../view/frontend/web/js/model/place-order.js | 2 +- .../web/js/model/postcode-validator.js | 37 +- .../view/frontend/web/js/model/quote.js | 125 +- .../web/js/model/resource-url-manager.js | 175 +- .../shipping-address/form-popup-state.js | 24 +- .../customer-address.js | 88 +- .../shipping-rate-processor/new-address.js | 131 +- .../web/js/model/shipping-rate-registry.js | 47 +- .../web/js/model/shipping-rate-service.js | 56 +- .../model/shipping-rates-validation-rules.js | 71 +- .../web/js/model/shipping-rates-validator.js | 367 +- .../web/js/model/shipping-save-processor.js | 59 +- .../model/shipping-save-processor/default.js | 120 +- .../frontend/web/js/model/shipping-service.js | 66 +- .../view/frontend/web/js/model/sidebar.js | 57 +- .../frontend/web/js/model/step-navigator.js | 294 +- .../view/frontend/web/js/model/totals.js | 90 +- .../view/frontend/web/js/model/url-builder.js | 84 +- .../frontend/web/js/proceed-to-checkout.js | 40 +- .../view/frontend/web/js/region-updater.js | 20 +- .../view/frontend/web/js/shopping-cart.js | 37 +- .../Checkout/view/frontend/web/js/sidebar.js | 96 +- .../web/js/view/authentication-messages.js | 5 +- .../frontend/web/js/view/authentication.js | 99 +- .../frontend/web/js/view/beforePlaceOrder.js | 24 +- .../frontend/web/js/view/billing-address.js | 432 +- .../web/js/view/cart/shipping-estimation.js | 12 +- .../web/js/view/cart/shipping-rates.js | 153 +- .../view/frontend/web/js/view/cart/totals.js | 2 +- .../web/js/view/cart/totals/shipping.js | 34 +- .../view/checkout/minicart/subtotal/totals.js | 2 +- .../view/configure/product-customer-data.js | 59 + .../view/frontend/web/js/view/estimation.js | 96 +- .../web/js/view/form/element/email.js | 8 +- .../view/frontend/web/js/view/minicart.js | 46 +- .../view/frontend/web/js/view/payment.js | 141 +- .../frontend/web/js/view/payment/default.js | 447 +- .../web/js/view/payment/email-validator.js | 2 +- .../view/frontend/web/js/view/payment/list.js | 144 +- .../view/frontend/web/js/view/progress-bar.js | 85 +- .../view/frontend/web/js/view/registration.js | 116 +- .../frontend/web/js/view/review/actions.js | 78 +- .../web/js/view/review/actions/default.js | 39 +- .../address-renderer/default.js | 39 +- .../web/js/view/shipping-address/list.js | 39 +- .../web/js/view/shipping-information.js | 80 +- .../address-renderer/default.js | 15 +- .../web/js/view/shipping-information/list.js | 36 +- .../view/frontend/web/js/view/shipping.js | 568 +- .../view/frontend/web/js/view/sidebar.js | 37 +- .../view/frontend/web/js/view/summary.js | 26 +- .../web/js/view/summary/abstract-total.js | 65 +- .../web/js/view/summary/cart-items.js | 104 +- .../web/js/view/summary/grand-total.js | 68 +- .../web/js/view/summary/item/details.js | 40 +- .../js/view/summary/item/details/subtotal.js | 42 +- .../js/view/summary/item/details/thumbnail.js | 122 +- .../frontend/web/js/view/summary/shipping.js | 86 +- .../frontend/web/js/view/summary/subtotal.js | 60 +- .../frontend/web/js/view/summary/totals.js | 33 +- .../frontend/web/template/authentication.html | 2 +- .../web/template/billing-address.html | 44 +- .../web/template/billing-address/details.html | 12 +- .../web/template/billing-address/form.html | 2 +- .../web/template/billing-address/list.html | 2 +- .../template/cart/shipping-estimation.html | 2 +- .../web/template/cart/shipping-rates.html | 2 +- .../frontend/web/template/cart/totals.html | 2 +- .../web/template/cart/totals/grand-total.html | 2 +- .../web/template/cart/totals/shipping.html | 2 +- .../web/template/cart/totals/subtotal.html | 2 +- .../frontend/web/template/estimation.html | 2 +- .../web/template/form/element/email.html | 2 +- .../web/template/minicart/content.html | 97 +- .../web/template/minicart/item/default.html | 5 +- .../web/template/minicart/item/price.html | 2 +- .../web/template/minicart/subtotal.html | 2 +- .../template/minicart/subtotal/totals.html | 2 +- .../view/frontend/web/template/onepage.html | 2 +- .../web/template/payment-methods/list.html | 22 +- .../view/frontend/web/template/payment.html | 3 +- .../template/payment/before-place-order.html | 4 +- .../web/template/payment/generic-title.html | 4 +- .../frontend/web/template/progress-bar.html | 2 +- .../frontend/web/template/registration.html | 2 +- .../frontend/web/template/review/actions.html | 4 +- .../web/template/review/actions/default.html | 2 +- .../address-renderer/default.html | 12 +- .../web/template/shipping-address/form.html | 2 +- .../web/template/shipping-address/list.html | 2 +- .../shipping-method-item.html | 39 + .../shipping-method-list.html | 23 + .../web/template/shipping-information.html | 2 +- .../address-renderer/default.html | 12 +- .../template/shipping-information/list.html | 2 +- .../view/frontend/web/template/shipping.html | 155 +- .../view/frontend/web/template/sidebar.html | 2 +- .../view/frontend/web/template/summary.html | 2 +- .../web/template/summary/cart-items.html | 67 +- .../web/template/summary/grand-total.html | 2 +- .../web/template/summary/item/details.html | 2 +- .../summary/item/details/subtotal.html | 4 +- .../summary/item/details/thumbnail.html | 2 +- .../web/template/summary/shipping.html | 2 +- .../web/template/summary/subtotal.html | 2 +- .../frontend/web/template/summary/totals.html | 2 +- .../CheckoutAgreementsRepositoryInterface.php | 2 +- .../Api/Data/AgreementInterface.php | 2 +- .../Block/Adminhtml/Agreement.php | 2 +- .../Block/Adminhtml/Agreement/Edit.php | 2 +- .../Block/Adminhtml/Agreement/Edit/Form.php | 2 +- .../Block/Adminhtml/Agreement/Grid.php | 2 +- .../CheckoutAgreements/Block/Agreements.php | 2 +- .../Controller/Adminhtml/Agreement.php | 2 +- .../Controller/Adminhtml/Agreement/Delete.php | 2 +- .../Controller/Adminhtml/Agreement/Edit.php | 2 +- .../Controller/Adminhtml/Agreement/Index.php | 2 +- .../Adminhtml/Agreement/NewAction.php | 2 +- .../Controller/Adminhtml/Agreement/Save.php | 2 +- .../CheckoutAgreements/Model/Agreement.php | 2 +- .../Model/AgreementModeOptions.php | 2 +- .../Model/AgreementsConfigProvider.php | 2 +- .../Model/AgreementsProvider.php | 2 +- .../Model/AgreementsProviderInterface.php | 2 +- .../Model/AgreementsValidator.php | 2 +- .../Model/Checkout/Plugin/Validation.php | 2 +- .../Model/CheckoutAgreementsRepository.php | 2 +- .../Model/ResourceModel/Agreement.php | 2 +- .../ResourceModel/Agreement/Collection.php | 2 +- .../Setup/InstallSchema.php | 2 +- .../Setup/UpgradeSchema.php | 2 +- .../Test/Unit/Block/AgreementsTest.php | 2 +- .../Unit/Model/AgreementModeOptionsTest.php | 2 +- .../Test/Unit/Model/AgreementTest.php | 2 +- .../Model/AgreementsConfigProviderTest.php | 2 +- .../Unit/Model/AgreementsProviderTest.php | 2 +- .../Unit/Model/AgreementsValidatorTest.php | 2 +- .../Model/Checkout/Plugin/ValidationTest.php | 2 +- .../CheckoutAgreementsRepositoryTest.php | 2 +- .../Magento/CheckoutAgreements/composer.json | 2 +- .../Magento/CheckoutAgreements/etc/acl.xml | 2 +- .../CheckoutAgreements/etc/adminhtml/menu.xml | 2 +- .../etc/adminhtml/routes.xml | 2 +- .../etc/adminhtml/system.xml | 2 +- .../Magento/CheckoutAgreements/etc/di.xml | 2 +- .../etc/extension_attributes.xml | 2 +- .../CheckoutAgreements/etc/frontend/di.xml | 2 +- .../Magento/CheckoutAgreements/etc/module.xml | 2 +- .../Magento/CheckoutAgreements/etc/webapi.xml | 2 +- .../CheckoutAgreements/registration.php | 2 +- .../frontend/layout/checkout_index_index.xml | 4 +- .../multishipping_checkout_overview.xml | 2 +- .../view/frontend/requirejs-config.js | 5 +- .../templates/additional_agreements.phtml | 2 +- .../view/frontend/templates/agreements.phtml | 2 +- .../templates/multishipping_agreements.phtml | 2 +- .../web/js/model/agreement-validator.js | 65 +- .../web/js/model/agreements-assigner.js | 2 +- .../frontend/web/js/model/agreements-modal.js | 83 +- .../web/js/model/place-order-mixin.js | 5 +- .../js/model/set-payment-information-mixin.js | 2 +- .../web/js/view/agreement-validation.js | 26 +- .../web/js/view/checkout-agreements.js | 101 +- .../checkout/checkout-agreements.html | 9 +- .../Cms/Api/BlockRepositoryInterface.php | 2 +- .../Magento/Cms/Api/Data/BlockInterface.php | 2 +- .../Api/Data/BlockSearchResultsInterface.php | 2 +- .../Magento/Cms/Api/Data/PageInterface.php | 2 +- .../Api/Data/PageSearchResultsInterface.php | 2 +- .../Cms/Api/PageRepositoryInterface.php | 2 +- .../Magento/Cms/Block/Adminhtml/Block.php | 2 +- .../Block/Adminhtml/Block/Edit/BackButton.php | 2 +- .../Adminhtml/Block/Edit/DeleteButton.php | 2 +- .../Adminhtml/Block/Edit/GenericButton.php | 2 +- .../Adminhtml/Block/Edit/ResetButton.php | 2 +- .../Block/Edit/SaveAndContinueButton.php | 2 +- .../Block/Adminhtml/Block/Edit/SaveButton.php | 2 +- .../Block/Adminhtml/Block/Widget/Chooser.php | 2 +- app/code/Magento/Cms/Block/Adminhtml/Page.php | 2 +- .../Block/Adminhtml/Page/Edit/BackButton.php | 2 +- .../Adminhtml/Page/Edit/DeleteButton.php | 2 +- .../Adminhtml/Page/Edit/GenericButton.php | 2 +- .../Block/Adminhtml/Page/Edit/ResetButton.php | 2 +- .../Page/Edit/SaveAndContinueButton.php | 2 +- .../Block/Adminhtml/Page/Edit/SaveButton.php | 2 +- .../Magento/Cms/Block/Adminhtml/Page/Grid.php | 2 +- .../Adminhtml/Page/Grid/Renderer/Action.php | 2 +- .../Page/Grid/Renderer/Action/UrlBuilder.php | 2 +- .../Block/Adminhtml/Page/Widget/Chooser.php | 2 +- .../Adminhtml/Wysiwyg/Images/Content.php | 2 +- .../Wysiwyg/Images/Content/Files.php | 2 +- .../Wysiwyg/Images/Content/Newfolder.php | 2 +- .../Wysiwyg/Images/Content/Uploader.php | 2 +- .../Block/Adminhtml/Wysiwyg/Images/Tree.php | 16 +- app/code/Magento/Cms/Block/Block.php | 2 +- app/code/Magento/Cms/Block/Page.php | 2 +- app/code/Magento/Cms/Block/Widget/Block.php | 2 +- .../Magento/Cms/Block/Widget/Page/Link.php | 2 +- .../Cms/Controller/Adminhtml/Block.php | 2 +- .../Cms/Controller/Adminhtml/Block/Delete.php | 2 +- .../Cms/Controller/Adminhtml/Block/Edit.php | 2 +- .../Cms/Controller/Adminhtml/Block/Index.php | 2 +- .../Controller/Adminhtml/Block/InlineEdit.php | 2 +- .../Controller/Adminhtml/Block/MassDelete.php | 2 +- .../Controller/Adminhtml/Block/NewAction.php | 2 +- .../Cms/Controller/Adminhtml/Block/Save.php | 2 +- .../Adminhtml/Block/Widget/Chooser.php | 2 +- .../Cms/Controller/Adminhtml/Page/Delete.php | 2 +- .../Cms/Controller/Adminhtml/Page/Edit.php | 2 +- .../Cms/Controller/Adminhtml/Page/Index.php | 2 +- .../Controller/Adminhtml/Page/InlineEdit.php | 7 +- .../Controller/Adminhtml/Page/MassDelete.php | 2 +- .../Controller/Adminhtml/Page/MassDisable.php | 2 +- .../Controller/Adminhtml/Page/MassEnable.php | 2 +- .../Controller/Adminhtml/Page/NewAction.php | 2 +- .../Adminhtml/Page/PostDataProcessor.php | 2 +- .../Cms/Controller/Adminhtml/Page/Save.php | 28 +- .../Adminhtml/Page/Widget/Chooser.php | 2 +- .../Adminhtml/Wysiwyg/Directive.php | 2 +- .../Controller/Adminhtml/Wysiwyg/Images.php | 2 +- .../Adminhtml/Wysiwyg/Images/Contents.php | 2 +- .../Adminhtml/Wysiwyg/Images/DeleteFiles.php | 2 +- .../Adminhtml/Wysiwyg/Images/DeleteFolder.php | 2 +- .../Adminhtml/Wysiwyg/Images/Index.php | 2 +- .../Adminhtml/Wysiwyg/Images/NewFolder.php | 2 +- .../Adminhtml/Wysiwyg/Images/OnInsert.php | 2 +- .../Adminhtml/Wysiwyg/Images/Thumbnail.php | 2 +- .../Adminhtml/Wysiwyg/Images/TreeJson.php | 2 +- .../Adminhtml/Wysiwyg/Images/Upload.php | 2 +- .../Cms/Controller/Index/DefaultIndex.php | 2 +- .../Cms/Controller/Index/DefaultNoRoute.php | 2 +- .../Magento/Cms/Controller/Index/Index.php | 2 +- .../Magento/Cms/Controller/Noroute/Index.php | 2 +- app/code/Magento/Cms/Controller/Page/View.php | 2 +- app/code/Magento/Cms/Controller/Router.php | 2 +- app/code/Magento/Cms/Helper/Page.php | 4 +- .../Magento/Cms/Helper/Wysiwyg/Images.php | 2 +- .../FilterProcessor/BlockStoreFilter.php | 2 +- .../FilterProcessor/PageStoreFilter.php | 2 +- app/code/Magento/Cms/Model/Block.php | 6 +- .../Magento/Cms/Model/Block/DataProvider.php | 2 +- .../Cms/Model/Block/Source/IsActive.php | 2 +- .../Magento/Cms/Model/BlockRepository.php | 2 +- .../Magento/Cms/Model/Config/Source/Page.php | 2 +- .../Model/Config/Source/Wysiwyg/Enabled.php | 2 +- app/code/Magento/Cms/Model/Page.php | 6 +- .../Magento/Cms/Model/Page/DataProvider.php | 2 +- .../Cms/Model/Page/Source/CustomLayout.php | 2 +- .../Cms/Model/Page/Source/IsActive.php | 2 +- .../Cms/Model/Page/Source/IsActiveFilter.php | 2 +- .../Cms/Model/Page/Source/PageLayout.php | 2 +- .../Model/Page/Source/PageLayoutFilter.php | 2 +- .../Magento/Cms/Model/Page/Source/Theme.php | 2 +- app/code/Magento/Cms/Model/PageRepository.php | 2 +- .../ResourceModel/AbstractCollection.php | 2 +- .../Magento/Cms/Model/ResourceModel/Block.php | 2 +- .../Model/ResourceModel/Block/Collection.php | 3 +- .../ResourceModel/Block/Grid/Collection.php | 15 +- .../Block/Relation/Store/ReadHandler.php | 2 +- .../Block/Relation/Store/SaveHandler.php | 2 +- .../Magento/Cms/Model/ResourceModel/Page.php | 2 +- .../Model/ResourceModel/Page/Collection.php | 2 +- .../ResourceModel/Page/Grid/Collection.php | 2 +- .../Page/Relation/Store/ReadHandler.php | 2 +- .../Page/Relation/Store/SaveHandler.php | 2 +- .../Magento/Cms/Model/Template/Filter.php | 2 +- .../Cms/Model/Template/FilterProvider.php | 2 +- app/code/Magento/Cms/Model/Wysiwyg/Config.php | 2 +- .../Cms/Model/Wysiwyg/Images/Storage.php | 2 +- .../Wysiwyg/Images/Storage/Collection.php | 2 +- .../Cms/Observer/NoCookiesObserver.php | 2 +- .../Magento/Cms/Observer/NoRouteObserver.php | 2 +- app/code/Magento/Cms/Setup/InstallData.php | 2 +- app/code/Magento/Cms/Setup/InstallSchema.php | 2 +- app/code/Magento/Cms/Setup/UpgradeData.php | 10 +- app/code/Magento/Cms/Setup/UpgradeSchema.php | 2 +- .../Adminhtml/Block/Widget/ChooserTest.php | 2 +- .../Adminhtml/Page/Widget/ChooserTest.php | 2 +- .../Magento/Cms/Test/Unit/Block/BlockTest.php | 2 +- .../Magento/Cms/Test/Unit/Block/PageTest.php | 2 +- .../Test/Unit/Block/Widget/Page/LinkTest.php | 2 +- .../Adminhtml/AbstractMassActionTest.php | 2 +- .../Controller/Adminhtml/Block/DeleteTest.php | 2 +- .../Controller/Adminhtml/Block/EditTest.php | 2 +- .../Adminhtml/Block/MassDeleteTest.php | 2 +- .../Controller/Adminhtml/Block/SaveTest.php | 2 +- .../Controller/Adminhtml/Page/DeleteTest.php | 2 +- .../Controller/Adminhtml/Page/EditTest.php | 2 +- .../Adminhtml/Page/InlineEditTest.php | 2 +- .../Adminhtml/Page/MassDeleteTest.php | 2 +- .../Adminhtml/Page/MassDisableTest.php | 2 +- .../Adminhtml/Page/MassEnableTest.php | 2 +- .../Controller/Adminhtml/Page/SaveTest.php | 176 +- .../Adminhtml/Wysiwyg/DirectiveTest.php | 2 +- .../Unit/Controller/Block/InlineEditTest.php | 2 +- .../Test/Unit/Controller/Index/IndexTest.php | 2 +- .../Unit/Controller/Noroute/IndexTest.php | 2 +- .../Controller/Page/PostDataProcessorTest.php | 2 +- .../Test/Unit/Controller/Page/ViewTest.php | 2 +- .../Cms/Test/Unit/Controller/RouterTest.php | 159 + .../Magento/Cms/Test/Unit/Helper/PageTest.php | 15 +- .../Test/Unit/Helper/Wysiwyg/ImagesTest.php | 2 +- .../FilterProcessor/BlockStoreFilterTest.php | 2 +- .../FilterProcessor/PageStoreFilterTest.php | 2 +- .../Unit/Model/Block/Source/IsActiveTest.php | 2 +- .../Test/Unit/Model/BlockRepositoryTest.php | 2 +- .../Unit/Model/Config/Source/PageTest.php | 2 +- .../Model/Page/Source/CustomLayoutTest.php | 2 +- .../Model/Page/Source/IsActiveFilterTest.php | 2 +- .../Unit/Model/Page/Source/IsActiveTest.php | 2 +- .../Page/Source/PageLayoutFilterTest.php | 2 +- .../Unit/Model/Page/Source/PageLayoutTest.php | 2 +- .../Test/Unit/Model/Page/Source/ThemeTest.php | 2 +- .../Test/Unit/Model/PageRepositoryTest.php | 2 +- .../Magento/Cms/Test/Unit/Model/PageTest.php | 2 +- .../ResourceModel/AbstractCollectionTest.php | 2 +- .../ResourceModel/Block/CollectionTest.php | 2 +- .../Block/Relation/Store/ReadHandlerTest.php | 2 +- .../Block/Relation/Store/SaveHandlerTest.php | 2 +- .../ResourceModel/Page/CollectionTest.php | 2 +- .../Page/Grid/CollectionTest.php | 2 +- .../Page/Relation/Store/ReadHandlerTest.php | 2 +- .../Page/Relation/Store/SaveHandlerTest.php | 2 +- .../Unit/Model/ResourceModel/PageTest.php | 2 +- .../Model/Template/FilterProviderTest.php | 2 +- .../Test/Unit/Model/Template/FilterTest.php | 4 +- .../Test/Unit/Model/Wysiwyg/ConfigTest.php | 2 +- .../Unit/Model/Wysiwyg/Images/StorageTest.php | 2 +- .../Unit/Observer/NoCookiesObserverTest.php | 2 +- .../Unit/Observer/NoRouteObserverTest.php | 2 +- .../Listing/Column/BlockActionsTest.php | 134 + .../Listing/Column/Cms/OptionsTest.php | 2 +- .../Listing/Column/PageActionsTest.php | 23 +- .../Ui/Component/Listing/DataProviderTest.php | 135 + .../Magento/Cms/Ui/Component/DataProvider.php | 51 +- .../Component/Listing/Column/BlockActions.php | 31 +- .../Component/Listing/Column/Cms/Options.php | 2 +- .../Component/Listing/Column/PageActions.php | 31 +- app/code/Magento/Cms/composer.json | 2 +- app/code/Magento/Cms/etc/acl.xml | 2 +- app/code/Magento/Cms/etc/adminhtml/di.xml | 2 +- app/code/Magento/Cms/etc/adminhtml/menu.xml | 2 +- app/code/Magento/Cms/etc/adminhtml/routes.xml | 2 +- app/code/Magento/Cms/etc/adminhtml/system.xml | 2 +- app/code/Magento/Cms/etc/config.xml | 2 +- app/code/Magento/Cms/etc/di.xml | 2 +- app/code/Magento/Cms/etc/events.xml | 2 +- app/code/Magento/Cms/etc/frontend/di.xml | 2 +- app/code/Magento/Cms/etc/frontend/events.xml | 2 +- .../Magento/Cms/etc/frontend/page_types.xml | 2 +- app/code/Magento/Cms/etc/frontend/routes.xml | 4 +- app/code/Magento/Cms/etc/module.xml | 2 +- app/code/Magento/Cms/etc/mview.xml | 2 +- app/code/Magento/Cms/etc/webapi.xml | 2 +- app/code/Magento/Cms/etc/widget.xml | 2 +- app/code/Magento/Cms/i18n/en_US.csv | 6 +- app/code/Magento/Cms/registration.php | 2 +- .../view/adminhtml/layout/cms_block_edit.xml | 2 +- .../view/adminhtml/layout/cms_block_index.xml | 2 +- .../view/adminhtml/layout/cms_block_new.xml | 2 +- .../view/adminhtml/layout/cms_page_edit.xml | 2 +- .../view/adminhtml/layout/cms_page_index.xml | 2 +- .../view/adminhtml/layout/cms_page_new.xml | 2 +- .../layout/cms_wysiwyg_images_contents.xml | 2 +- .../layout/cms_wysiwyg_images_index.xml | 2 +- .../Cms/view/adminhtml/requirejs-config.js | 4 +- .../adminhtml/templates/browser/content.phtml | 2 +- .../templates/browser/content/files.phtml | 6 +- .../templates/browser/content/uploader.phtml | 7 +- .../adminhtml/templates/browser/tree.phtml | 6 +- .../page/edit/form/renderer/content.phtml | 2 +- .../adminhtml/ui_component/cms_block_form.xml | 2 +- .../ui_component/cms_block_listing.xml | 2 +- .../adminhtml/ui_component/cms_page_form.xml | 2 +- .../ui_component/cms_page_listing.xml | 2 +- .../Cms/view/adminhtml/web/js/folder-tree.js | 147 +- .../layout/cms_index_defaultindex.xml | 2 +- .../layout/cms_index_defaultnoroute.xml | 2 +- .../view/frontend/layout/cms_index_index.xml | 2 +- .../frontend/layout/cms_index_nocookies.xml | 2 +- .../frontend/layout/cms_index_noroute.xml | 2 +- .../view/frontend/layout/cms_page_view.xml | 2 +- .../Cms/view/frontend/layout/default.xml | 2 +- .../Cms/view/frontend/layout/print.xml | 2 +- .../Cms/view/frontend/templates/content.phtml | 2 +- .../frontend/templates/default/home.phtml | 4 +- .../frontend/templates/default/no-route.phtml | 2 +- .../Cms/view/frontend/templates/meta.phtml | 2 +- .../templates/widget/link/link_block.phtml | 8 +- .../templates/widget/link/link_inline.phtml | 8 +- .../widget/static_block/default.phtml | 2 +- .../Model/CmsPageUrlPathGenerator.php | 2 +- .../Model/CmsPageUrlRewriteGenerator.php | 2 +- .../ProcessUrlRewriteSavingObserver.php | 2 +- .../Plugin/Cms/Model/ResourceModel/Page.php | 2 +- .../Model/CmsPageUrlRewriteGeneratorTest.php | 133 + .../ProcessUrlRewriteSavingObserverTest.php | 2 +- .../Cms/Model/ResourceModel/PageTest.php | 2 +- app/code/Magento/CmsUrlRewrite/composer.json | 2 +- .../CmsUrlRewrite/etc/adminhtml/di.xml | 2 +- app/code/Magento/CmsUrlRewrite/etc/events.xml | 2 +- app/code/Magento/CmsUrlRewrite/etc/module.xml | 2 +- .../Magento/CmsUrlRewrite/registration.php | 2 +- .../Source/DumpConfigSourceAggregated.php | 152 + .../Source/DumpConfigSourceInterface.php | 21 + .../Config/Source/EnvironmentConfigSource.php | 79 + .../App/Config/Source/ModularConfigSource.php | 44 + .../App/Config/Source/RuntimeConfigSource.php | 98 + .../Magento/Config/App/Config/Type/System.php | 128 + .../Config/Block/System/Config/Dwstree.php | 2 +- .../Config/Block/System/Config/Edit.php | 2 +- .../Config/Block/System/Config/Form.php | 189 +- .../Config/Block/System/Config/Form/Field.php | 7 +- .../System/Config/Form/Field/Datetime.php | 2 +- .../System/Config/Form/Field/Factory.php | 2 +- .../Field/FieldArray/AbstractFieldArray.php | 10 +- .../Block/System/Config/Form/Field/File.php | 2 +- .../System/Config/Form/Field/Heading.php | 2 +- .../Block/System/Config/Form/Field/Image.php | 2 +- .../System/Config/Form/Field/Notification.php | 2 +- .../Config/Form/Field/Regexceptions.php | 2 +- .../Form/Field/Select/Allowspecific.php | 2 +- .../Block/System/Config/Form/Fieldset.php | 2 +- .../System/Config/Form/Fieldset/Factory.php | 2 +- .../Form/Fieldset/Modules/DisableOutput.php | 21 +- .../Config/Block/System/Config/Tabs.php | 2 +- .../ConfigSet/ConfigSetProcessorFactory.php | 79 + .../ConfigSet/ConfigSetProcessorInterface.php | 29 + .../Command/ConfigSet/DefaultProcessor.php | 120 + .../Command/ConfigSet/LockProcessor.php | 134 + .../Console/Command/ConfigSetCommand.php | 145 + .../Command/ConfigShow/ValueProcessor.php | 89 + .../Console/Command/ConfigShowCommand.php | 206 + .../Magento/Config/Console/CommandList.php | 48 + .../Adminhtml/System/AbstractConfig.php | 2 +- .../System/Config/AbstractScopeConfig.php | 2 +- .../Adminhtml/System/Config/Edit.php | 2 +- .../Adminhtml/System/Config/Index.php | 2 +- .../Adminhtml/System/Config/Save.php | 2 +- .../Adminhtml/System/Config/State.php | 2 +- .../Adminhtml/System/ConfigSectionChecker.php | 2 +- app/code/Magento/Config/Model/Config.php | 2 +- .../Model/Config/Backend/Admin/Custom.php | 2 +- .../Model/Config/Backend/Admin/Custompath.php | 2 +- .../Admin/Password/Link/Expirationperiod.php | 2 +- .../Model/Config/Backend/Admin/Robots.php | 2 +- .../Model/Config/Backend/Admin/Usecustom.php | 2 +- .../Config/Backend/Admin/Usesecretkey.php | 2 +- .../Config/Model/Config/Backend/Baseurl.php | 27 +- .../Config/Model/Config/Backend/Cache.php | 2 +- .../Backend/Currency/AbstractCurrency.php | 5 +- .../Model/Config/Backend/Currency/Allow.php | 2 +- .../Model/Config/Backend/Currency/Base.php | 2 +- .../Model/Config/Backend/Currency/Cron.php | 2 +- .../Backend/Currency/DefaultCurrency.php | 2 +- .../Config/Model/Config/Backend/Datashare.php | 2 +- .../Model/Config/Backend/Design/Exception.php | 2 +- .../Model/Config/Backend/Email/Address.php | 2 +- .../Model/Config/Backend/Email/Logo.php | 2 +- .../Model/Config/Backend/Email/Sender.php | 2 +- .../Config/Model/Config/Backend/Encrypted.php | 2 +- .../Config/Model/Config/Backend/File.php | 2 +- .../Model/Config/Backend/File/RequestData.php | 2 +- .../File/RequestData/RequestDataInterface.php | 2 +- .../Config/Model/Config/Backend/Filename.php | 2 +- .../Config/Model/Config/Backend/Image.php | 2 +- .../Model/Config/Backend/Image/Adapter.php | 2 +- .../Model/Config/Backend/Image/Favicon.php | 2 +- .../Model/Config/Backend/Image/Logo.php | 2 +- .../Config/Model/Config/Backend/Image/Pdf.php | 2 +- .../Config/Model/Config/Backend/Locale.php | 2 +- .../Model/Config/Backend/Locale/Timezone.php | 2 +- .../Config/Model/Config/Backend/Log/Cron.php | 2 +- .../Config/Model/Config/Backend/Secure.php | 2 +- .../Model/Config/Backend/Serialized.php | 47 +- .../Backend/Serialized/ArraySerialized.php | 2 +- .../Config/Model/Config/Backend/Store.php | 7 +- .../Config/Model/Config/Backend/Translate.php | 2 +- .../Model/Config/BackendClone/Factory.php | 2 +- .../Config/Model/Config/BackendFactory.php | 2 +- .../Config/Model/Config/CommentFactory.php | 2 +- .../Config/Model/Config/CommentInterface.php | 2 +- .../Model/Config/Compiler/IncludeElement.php | 2 +- .../Config/Model/Config/Export/Comment.php | 59 + .../Model/Config/Export/ExcludeList.php | 55 + .../Magento/Config/Model/Config/Factory.php | 2 +- .../Magento/Config/Model/Config/Loader.php | 7 +- .../Config/Model/Config/Parser/Comment.php | 109 + .../Processor/EnvironmentPlaceholder.php | 70 + .../Reader/Source/Deployed/SettingChecker.php | 119 + .../Config/Model/Config/SchemaLocator.php | 2 +- .../Config/Model/Config/ScopeDefiner.php | 2 +- .../Config/Model/Config/Source/Admin/Page.php | 2 +- .../Config/Model/Config/Source/Date/Short.php | 2 +- .../Model/Config/Source/Design/Robots.php | 2 +- .../Model/Config/Source/Dev/Dbautoup.php | 2 +- .../Model/Config/Source/Email/Identity.php | 2 +- .../Model/Config/Source/Email/Method.php | 2 +- .../Model/Config/Source/Email/Smtpauth.php | 2 +- .../Model/Config/Source/Email/Template.php | 2 +- .../Model/Config/Source/Enabledisable.php | 2 +- .../Model/Config/Source/Image/Adapter.php | 2 +- .../Config/Model/Config/Source/Locale.php | 2 +- .../Model/Config/Source/Locale/Country.php | 2 +- .../Model/Config/Source/Locale/Currency.php | 2 +- .../Config/Source/Locale/Currency/All.php | 2 +- .../Model/Config/Source/Locale/Timezone.php | 2 +- .../Config/Source/Locale/Weekdaycodes.php | 2 +- .../Model/Config/Source/Locale/Weekdays.php | 2 +- .../Config/Model/Config/Source/Nooptreq.php | 2 +- .../Model/Config/Source/Reports/Scope.php | 2 +- .../Config/Model/Config/Source/Store.php | 2 +- .../Model/Config/Source/Web/Protocol.php | 2 +- .../Model/Config/Source/Web/Redirect.php | 2 +- .../Config/Model/Config/Source/Website.php | 2 +- .../Config/Source/Website/AdminOptionHash.php | 2 +- .../Config/Source/Website/OptionHash.php | 2 +- .../Config/Model/Config/Source/Yesno.php | 2 +- .../Model/Config/Source/Yesnocustom.php | 2 +- .../Config/Model/Config/SourceFactory.php | 2 +- .../Magento/Config/Model/Config/Structure.php | 2 +- .../Config/Structure/AbstractElement.php | 29 +- .../Model/Config/Structure/AbstractMapper.php | 2 +- .../ConcealInProductionConfigList.php | 97 + .../Model/Config/Structure/Converter.php | 2 +- .../Config/Model/Config/Structure/Data.php | 15 +- .../Structure/Element/AbstractComposite.php | 2 +- .../Structure/Element/Dependency/Field.php | 2 +- .../Element/Dependency/FieldFactory.php | 2 +- .../Structure/Element/Dependency/Mapper.php | 2 +- .../Model/Config/Structure/Element/Field.php | 2 +- .../Structure/Element/FlyweightFactory.php | 2 +- .../Model/Config/Structure/Element/Group.php | 2 +- .../Config/Structure/Element/Group/Proxy.php | 2 +- .../Config/Structure/Element/Iterator.php | 2 +- .../Structure/Element/Iterator/Field.php | 2 +- .../Structure/Element/Iterator/Group.php | 2 +- .../Structure/Element/Iterator/Section.php | 2 +- .../Config/Structure/Element/Iterator/Tab.php | 2 +- .../Config/Structure/Element/Section.php | 2 +- .../Model/Config/Structure/Element/Tab.php | 2 +- .../Config/Structure/ElementInterface.php | 2 +- .../Structure/ElementVisibilityComposite.php | 75 + .../Structure/ElementVisibilityInterface.php | 36 + .../Mapper/Attribute/Inheritance.php | 2 +- .../Config/Structure/Mapper/Dependencies.php | 2 +- .../Config/Structure/Mapper/ExtendsMapper.php | 2 +- .../Model/Config/Structure/Mapper/Factory.php | 2 +- .../Mapper/Helper/RelativePathConverter.php | 2 +- .../Model/Config/Structure/Mapper/Ignore.php | 2 +- .../Model/Config/Structure/Mapper/Path.php | 2 +- .../Model/Config/Structure/Mapper/Sorting.php | 2 +- .../Config/Structure/MapperInterface.php | 2 +- .../Config/Model/Config/Structure/Reader.php | 2 +- .../Model/Config/Structure/Search/Proxy.php | 2 +- .../Config/Structure/SearchInterface.php | 2 +- .../Config/Model/Placeholder/Environment.php | 76 + .../Model/Placeholder/PlaceholderFactory.php | 59 + .../Placeholder/PlaceholderInterface.php | 40 + .../Config/Model/ResourceModel/Config.php | 2 +- .../Model/ResourceModel/Config/Data.php | 2 +- .../ResourceModel/Config/Data/Collection.php | 2 +- .../Admin/AfterCustomUrlChangedObserver.php | 2 +- app/code/Magento/Config/Setup/InstallData.php | 2 +- .../Magento/Config/Setup/InstallSchema.php | 2 +- .../Source/DumpConfigSourceAggregatedTest.php | 171 + .../Source/EnvironmentConfigSourceTest.php | 113 + .../Config/Source/ModularConfigSourceTest.php | 43 + .../Config/Source/RuntimeConfigSourceTest.php | 136 + .../Test/Unit/App/Config/Type/SystemTest.php | 153 + .../Unit/Block/System/Config/DwstreeTest.php | 2 +- .../Unit/Block/System/Config/EditTest.php | 2 +- .../Form/Field/FieldArray/AbstractTest.php | 31 +- .../System/Config/Form/Field/FileTest.php | 2 +- .../System/Config/Form/Field/HeadingTest.php | 2 +- .../System/Config/Form/Field/ImageTest.php | 2 +- .../Config/Form/Field/NotificationTest.php | 2 +- .../Config/Form/Field/RegexceptionsTest.php | 2 +- .../Form/Field/Select/AllowspecificTest.php | 2 +- .../Block/System/Config/Form/FieldTest.php | 11 +- .../Fieldset/Modules/DisableOutputTest.php | 3 +- .../Block/System/Config/Form/FieldsetTest.php | 2 +- .../Unit/Block/System/Config/FormTest.php | 136 +- .../Unit/Block/System/Config/TabsTest.php | 2 +- .../ConfigSetProcessorFactoryTest.php | 88 + .../ConfigSet/DefaultProcessorTest.php | 164 + .../Command/ConfigSet/LockProcessorTest.php | 317 + .../Console/Command/ConfigSetCommandTest.php | 123 + .../Command/ConfigShow/ValueProcessorTest.php | 150 + .../Console/Command/ConfigShowCommandTest.php | 164 + .../Test/Unit/Console/CommandListTest.php | 53 + .../Adminhtml/System/Config/SaveTest.php | 2 +- .../System/Config/_files/expected_array.php | 2 +- .../System/Config/_files/files_array.php | 2 +- .../System/Config/_files/groups_array.php | 2 +- .../Model/Compiler/IncludeElementTest.php | 2 +- .../Unit/Model/Config/Backend/BaseurlTest.php | 2 +- .../Model/Config/Backend/Email/LogoTest.php | 2 +- .../Model/Config/Backend/EncryptedTest.php | 2 +- .../Config/Backend/File/RequestDataTest.php | 2 +- .../Unit/Model/Config/Backend/FileTest.php | 2 +- .../Model/Config/Backend/Image/LogoTest.php | 2 +- .../Unit/Model/Config/Backend/SecureTest.php | 2 +- .../Model/Config/Backend/SerializedTest.php | 106 + .../Unit/Model/Config/Export/CommentTest.php | 88 + .../Model/Config/Export/ExcludeListTest.php | 40 + .../Test/Unit/Model/Config/LoaderTest.php | 6 +- .../Unit/Model/Config/Parser/CommentTest.php | 83 + .../Processor/EnvironmentPlaceholderTest.php | 131 + .../Source/Deployed/SettingCheckerTest.php | 146 + .../Unit/Model/Config/SchemaLocatorTest.php | 2 +- .../Unit/Model/Config/ScopeDefinerTest.php | 2 +- .../Model/Config/Source/Admin/PageTest.php | 2 +- .../Config/Source/Email/TemplateTest.php | 2 +- .../Config/Source/Locale/TimezoneTest.php | 2 +- .../Config/Structure/AbstractElementTest.php | 26 +- .../ConcealInProductionConfigListTest.php | 103 + .../Model/Config/Structure/ConverterTest.php | 2 +- .../Element/AbstractCompositeTest.php | 19 +- .../Element/Dependency/FieldTest.php | 2 +- .../Element/Dependency/MapperTest.php | 2 +- .../Config/Structure/Element/FieldTest.php | 2 +- .../Element/FlyweightFactoryTest.php | 2 +- .../Structure/Element/Group/ProxyTest.php | 2 +- .../Config/Structure/Element/GroupTest.php | 2 +- .../Structure/Element/Iterator/FieldTest.php | 2 +- .../Config/Structure/Element/IteratorTest.php | 2 +- .../Config/Structure/Element/SectionTest.php | 16 +- .../Config/Structure/Element/TabTest.php | 2 +- .../ElementVisibilityCompositeTest.php | 111 + .../Structure/Mapper/DependenciesTest.php | 2 +- .../Config/Structure/Mapper/ExtendsTest.php | 2 +- .../Helper/RelativePathConverterTest.php | 2 +- .../Config/Structure/Mapper/PathTest.php | 2 +- .../Config/Structure/Mapper/SortingTest.php | 2 +- .../Model/Config/Structure/ReaderTest.php | 2 +- .../Test/Unit/Model/Config/StructureTest.php | 2 +- .../Config/Test/Unit/Model/Config/XsdTest.php | 2 +- .../Unit/Model/Config/_files/config.local.php | 24 + .../Config/_files/invalidSystemXmlArray.php | 2 +- .../Unit/Model/Config/_files/valid_system.xml | 2 +- .../Config/Test/Unit/Model/ConfigTest.php | 2 +- .../Model/Placeholder/EnvironmentTest.php | 131 + .../Placeholder/PlaceholderFactoryTest.php | 76 + .../Config/Test/Unit/Model/_files/acl.xml | 2 +- .../Config/Test/Unit/Model/_files/acl_1.xml | 2 +- .../Config/Test/Unit/Model/_files/acl_2.xml | 2 +- .../Test/Unit/Model/_files/acl_merged.xml | 2 +- .../Unit/Model/_files/converted_config.php | 2 +- .../Unit/Model/_files/dependencies_data.php | 2 +- .../Unit/Model/_files/dependencies_mapped.php | 2 +- .../Config/Test/Unit/Model/_files/menu_1.xml | 2 +- .../Config/Test/Unit/Model/_files/menu_2.xml | 2 +- .../Test/Unit/Model/_files/system_1.xml | 2 +- .../Test/Unit/Model/_files/system_2.xml | 2 +- .../Model/_files/system_config_options_1.xml | 2 +- .../Model/_files/system_config_options_2.xml | 2 +- .../_files/system_unknown_attribute_1.xml | 2 +- .../_files/system_unknown_attribute_2.xml | 2 +- app/code/Magento/Config/cli_commands.php | 9 + app/code/Magento/Config/composer.json | 6 +- app/code/Magento/Config/etc/acl.xml | 2 +- app/code/Magento/Config/etc/adminhtml/di.xml | 10 +- .../Magento/Config/etc/adminhtml/events.xml | 2 +- .../Magento/Config/etc/adminhtml/menu.xml | 2 +- .../Magento/Config/etc/adminhtml/routes.xml | 2 +- app/code/Magento/Config/etc/di.xml | 162 +- app/code/Magento/Config/etc/module.xml | 2 +- app/code/Magento/Config/etc/system.xsd | 2 +- app/code/Magento/Config/etc/system_file.xsd | 2 +- .../Magento/Config/etc/system_include.xsd | 2 +- app/code/Magento/Config/registration.php | 2 +- .../layout/adminhtml_system_config_edit.xml | 2 +- .../page/system/config/robots/reset.phtml | 2 +- .../templates/system/config/edit.phtml | 2 +- .../system/config/form/field/array.phtml | 4 +- .../templates/system/config/js.phtml | 2 +- .../templates/system/config/switcher.phtml | 2 +- .../templates/system/config/tabs.phtml | 2 +- .../Export/Product/Type/Configurable.php | 2 +- .../Model/Export/RowCustomizer.php | 56 +- .../Import/Product/Type/Configurable.php | 50 +- .../Unit/Model/Export/RowCustomizerTest.php | 319 +- .../Import/Product/Type/ConfigurableTest.php | 16 +- .../ConfigurableImportExport/composer.json | 2 +- .../ConfigurableImportExport/etc/di.xml | 2 +- .../ConfigurableImportExport/etc/export.xml | 2 +- .../ConfigurableImportExport/etc/import.xml | 2 +- .../ConfigurableImportExport/etc/module.xml | 2 +- .../ConfigurableImportExport/registration.php | 2 +- ...ConfigurableProductManagementInterface.php | 2 +- .../ConfigurableItemOptionValueInterface.php | 2 +- .../Api/Data/OptionInterface.php | 2 +- .../Api/Data/OptionValueInterface.php | 2 +- .../Api/LinkManagementInterface.php | 2 +- .../Api/OptionRepositoryInterface.php | 2 +- .../Block/Adminhtml/Order/Create/Sidebar.php | 2 +- .../Attribute/Edit/Tab/Variations/Main.php | 2 +- .../NewAttribute/Product/Created.php | 2 +- .../Composite/Fieldset/Configurable.php | 2 +- .../Product/Edit/AttributeSet/Form.php | 2 +- .../Adminhtml/Product/Edit/Button/Save.php | 2 +- .../Product/Edit/Tab/Variations/Config.php | 2 +- .../Edit/Tab/Variations/Config/Matrix.php | 2 +- .../Product/Steps/AttributeValues.php | 2 +- .../Block/Adminhtml/Product/Steps/Bulk.php | 2 +- .../Product/Steps/SelectAttributes.php | 2 +- .../Block/Adminhtml/Product/Steps/Summary.php | 2 +- .../Block/Cart/Item/Renderer/Configurable.php | 2 +- .../Block/Plugin/Product/Media/Gallery.php | 102 - .../AssociatedSelector/Renderer/Id.php | 2 +- .../Configurable/AttributeSelector.php | 2 +- .../Block/Product/View/Type/Configurable.php | 40 +- .../Block/Stockqty/Type/Configurable.php | 2 +- .../Adminhtml/Product/AddAttribute.php | 2 +- .../Adminhtml/Product/Associated/Grid.php | 2 +- .../Product/Attribute/CreateOptions.php | 2 +- .../Product/Attribute/GetAttributes.php | 2 +- .../SuggestConfigurableAttributes.php | 2 +- .../Adminhtml/Product/Builder/Plugin.php | 2 +- .../Helper/Plugin/Configurable.php | 2 +- .../Helper/Plugin/UpdateConfigurations.php | 26 +- .../Controller/Adminhtml/Product/Wizard.php | 2 +- .../CustomerData/ConfigurableItem.php | 2 +- .../ConfigurableProduct/Helper/Data.php | 8 +- .../Helper/Product/Configuration/Plugin.php | 2 +- .../Helper/Product/Options/Factory.php | 2 +- .../Helper/Product/Options/Loader.php | 2 +- .../Model/Attribute/LockValidator.php | 2 +- .../Model/AttributeOptionProvider.php | 139 + .../AttributeOptionProviderInterface.php | 21 + .../Model/AttributesList.php | 2 +- .../Model/AttributesListInterface.php | 2 +- .../Model/ConfigurableAttributeData.php | 2 +- .../Model/ConfigurableAttributeHandler.php | 2 +- .../Model/ConfigurableProductManagement.php | 2 +- .../Group/AttributeMapper/Plugin.php | 2 +- .../Model/LinkManagement.php | 103 +- .../Model/OptionRepository.php | 2 +- .../Order/Admin/Item/Plugin/Configurable.php | 2 +- .../Model/Plugin/PriceBackend.php | 2 +- .../Model/Plugin/ProductRepositorySave.php | 2 +- .../Model/Product/Cache/Tag/Configurable.php | 51 + .../CartConfiguration/Plugin/Configurable.php | 2 +- .../Model/Product/ReadHandler.php | 2 +- .../Model/Product/SaveHandler.php | 2 +- .../Model/Product/Type/Configurable.php | 81 +- .../Product/Type/Configurable/Attribute.php | 2 +- .../Product/Type/Configurable/OptionValue.php | 2 +- .../Model/Product/Type/Configurable/Price.php | 2 +- .../Model/Product/Type/Plugin.php | 2 +- .../Model/Product/Type/VariationMatrix.php | 2 +- .../Plugin/Configurable.php | 2 +- .../Model/Product/Validator/Plugin.php | 2 +- .../Model/Product/VariationHandler.php | 11 +- .../Model/ProductOptionProcessor.php | 2 +- .../Model/ProductVariationsBuilder.php | 2 +- .../Model/Quote/Item/CartItemProcessor.php | 16 +- .../Item/ConfigurableItemOptionValue.php | 2 +- .../Option/Plugin/ConfigurableProduct.php | 2 +- .../Attribute/OptionProvider.php | 38 + .../Indexer/Stock/Configurable.php | 11 +- .../Product/Indexer/Price/Configurable.php | 134 +- .../Product/Type/Configurable.php | 180 +- .../Product/Type/Configurable/Attribute.php | 2 +- .../Configurable/Attribute/Collection.php | 2 +- .../Type/Configurable/Product/Collection.php | 86 +- .../ResourceModel/Setup/PropertyMapper.php | 2 +- .../Model/SuggestedAttributeList.php | 2 +- .../HideUnsupportedAttributeTypes.php | 2 +- .../Attribute/Backend/AttributeValidation.php | 55 + .../Plugin/Model/Product.php | 35 - .../Plugin/Model/ResourceModel/Product.php | 2 +- .../Price/ConfigurableOptionsProvider.php | 39 +- .../ConfigurableOptionsProviderInterface.php | 3 +- .../Price/ConfigurablePriceResolver.php | 33 +- .../Price/ConfigurableRegularPrice.php | 20 +- .../ConfigurableRegularPriceInterface.php | 2 +- .../Pricing/Price/FinalPrice.php | 2 +- .../Pricing/Price/FinalPriceResolver.php | 2 +- .../Price/LowestPriceOptionsProvider.php | 63 + .../LowestPriceOptionsProviderInterface.php | 21 + .../Pricing/Price/PriceResolverInterface.php | 2 +- .../Pricing/Price/RegularPriceResolver.php | 2 +- .../Pricing/Render/FinalPriceBox.php | 18 +- .../Pricing/Render/TierPriceBox.php | 25 + .../ConfigurableProduct/Setup/InstallData.php | 2 +- .../Setup/InstallSchema.php | 2 +- .../ConfigurableProduct/Setup/Recurring.php | 2 +- .../ConfigurableProduct/Setup/UpgradeData.php | 2 +- .../Product/Edit/Button/SaveTest.php | 2 +- .../Edit/Tab/Variations/Config/MatrixTest.php | 2 +- .../Cart/Item/Renderer/ConfigurableTest.php | 2 +- .../Plugin/Product/Media/GalleryTest.php | 111 - .../Configurable/AttributeSelectorTest.php | 2 +- .../Adminhtml/Product/AddAttributeTest.php | 2 +- .../SuggestConfigurableAttributesTest.php | 2 +- .../Adminhtml/Product/Builder/PluginTest.php | 2 +- .../Helper/Plugin/ConfigurableTest.php | 2 +- .../Plugin/UpdateConfigurationsTest.php | 71 +- .../Test/Unit/Helper/DataTest.php | 74 +- .../Product/Configuration/PluginTest.php | 2 +- .../Helper/Product/Options/FactoryTest.php | 2 +- .../Helper/Product/Options/LoaderTest.php | 2 +- .../Model/Attribute/LockValidatorTest.php | 2 +- .../Model/AttributeOptionProviderTest.php | 215 + .../Test/Unit/Model/AttributesListTest.php | 2 +- .../Model/ConfigurableAttributeDataTest.php | 2 +- .../ConfigurableProductManagementTest.php | 2 +- .../Group/AttributeMapper/PluginTest.php | 2 +- .../Test/Unit/Model/LinkManagementTest.php | 91 +- .../Test/Unit/Model/OptionRepositoryTest.php | 2 +- .../Admin/Item/Plugin/ConfigurableTest.php | 2 +- .../Unit/Model/Plugin/PriceBackendTest.php | 2 +- .../Plugin/ProductRepositorySaveTest.php | 2 +- .../Product/Cache/Tag/ConfigurableTest.php | 68 + .../Plugin/ConfigurableTest.php | 2 +- .../Product/ProductExtensionAttributes.php | 2 +- .../ProductOptionExtensionAttributes.php | 2 +- .../Unit/Model/Product/ReadHandlerTest.php | 2 +- .../Unit/Model/Product/SaveHandlerTest.php | 2 +- .../Product/Type/Configurable/PriceTest.php | 2 +- .../Model/Product/Type/ConfigurableTest.php | 563 +- .../Unit/Model/Product/Type/PluginTest.php | 2 +- .../Product/Type/VariationMatrixTest.php | 2 +- .../Plugin/ConfigurableTest.php | 2 +- .../Model/Product/Validator/PluginTest.php | 2 +- .../Model/Product/VariationHandlerTest.php | 2 +- .../Unit/Model/ProductOptionProcessorTest.php | 2 +- .../Model/ProductVariationsBuilderTest.php | 2 +- .../Quote/Item/CartItemProcessorTest.php | 36 +- .../Option/Plugin/ConfigurableProductTest.php | 2 +- .../Attribute/OptionProviderTest.php | 65 + .../Type/Configurable/AttributeTest.php | 2 +- .../Product/Type/ConfigurableTest.php | 376 +- .../Unit/Model/SuggestedAttributeListTest.php | 2 +- .../HideUnsupportedAttributeTypesTest.php | 2 +- .../Model/ResourceModel/ProductTest.php | 2 +- .../Price/ConfigurablePriceResolverTest.php | 44 +- .../Unit/Pricing/Render/FinalPriceBoxTest.php | 28 +- .../Columns/AttributesTest.php | 2 +- .../AssociatedProduct/Columns/NameTest.php | 2 +- .../AssociatedProduct/Columns/PriceTest.php | 2 +- .../Product/Form/Modifier/CompositeTest.php | 2 +- .../ConfigurableAttributeSetHandlerTest.php | 2 +- .../Form/Modifier/ConfigurablePanelTest.php | 2 +- .../Form/Modifier/ConfigurablePriceTest.php | 2 +- .../Form/Modifier/ConfigurableQtyTest.php | 2 +- .../Form/Modifier/CustomOptionsTest.php | 2 +- .../Product/Form/Modifier/StockDataTest.php | 2 +- .../Attribute/Repository.php | 2 +- .../Listing/AssociatedProduct/Columns.php | 2 +- .../AssociatedProduct/Columns/Attributes.php | 2 +- .../AssociatedProduct/Columns/Name.php | 2 +- .../AssociatedProduct/Columns/Price.php | 2 +- .../Listing/AssociatedProduct/Filters.php | 2 +- .../Ui/DataProvider/Attributes.php | 2 +- .../Product/Form/Modifier/Composite.php | 2 +- .../ConfigurableAttributeSetHandler.php | 2 +- .../Form/Modifier/ConfigurablePanel.php | 2 +- .../Form/Modifier/ConfigurablePrice.php | 2 +- .../Product/Form/Modifier/ConfigurableQty.php | 2 +- .../Product/Form/Modifier/CustomOptions.php | 2 +- .../Form/Modifier/Data/AssociatedProducts.php | 2 +- .../Product/Form/Modifier/StockData.php | 2 +- .../Magento/ConfigurableProduct/composer.json | 2 +- .../ConfigurableProduct/etc/adminhtml/di.xml | 2 +- .../etc/adminhtml/events.xml | 2 +- .../etc/adminhtml/routes.xml | 2 +- .../etc/adminhtml/system.xml | 2 +- .../ConfigurableProduct/etc/config.xml | 2 +- .../Magento/ConfigurableProduct/etc/di.xml | 25 +- .../etc/extension_attributes.xml | 2 +- .../ConfigurableProduct/etc/frontend/di.xml | 2 +- .../ConfigurableProduct/etc/module.xml | 2 +- .../ConfigurableProduct/etc/product_types.xml | 2 +- .../Magento/ConfigurableProduct/etc/sales.xml | 2 +- .../ConfigurableProduct/etc/webapi.xml | 2 +- .../ConfigurableProduct/registration.php | 2 +- .../layout/catalog_product_addattribute.xml | 2 +- .../catalog_product_associated_grid.xml | 2 +- ...bute_edit_product_tab_variations_popup.xml | 2 +- .../layout/catalog_product_configurable.xml | 2 +- .../layout/catalog_product_downloadable.xml | 2 +- .../adminhtml/layout/catalog_product_new.xml | 2 +- .../layout/catalog_product_set_edit.xml | 2 +- .../layout/catalog_product_simple.xml | 2 +- .../catalog_product_superconfig_config.xml | 2 +- ...catalog_product_view_type_configurable.xml | 2 +- .../layout/catalog_product_virtual.xml | 2 +- .../layout/catalog_product_wizard.xml | 2 +- .../product/attribute/new/created.phtml | 4 +- .../catalog/product/attribute/set/js.phtml | 2 +- .../composite/fieldset/configurable.phtml | 2 +- .../attribute/steps/attributes_values.phtml | 2 +- .../product/edit/attribute/steps/bulk.phtml | 2 +- .../attribute/steps/select_attributes.phtml | 2 +- .../edit/attribute/steps/summary.phtml | 2 +- .../catalog/product/edit/super/config.phtml | 2 +- .../catalog/product/edit/super/matrix.phtml | 2 +- .../product/edit/super/wizard-ajax.phtml | 2 +- .../catalog/product/edit/super/wizard.phtml | 2 +- .../form.phtml | 2 +- .../affected-attribute-set-selector/js.phtml | 2 +- .../configurable/attribute-selector/js.phtml | 2 +- .../product/configurable/stock/disabler.phtml | 2 +- ...onfigurable_associated_product_listing.xml | 2 +- .../product_attributes_listing.xml | 2 +- .../adminhtml/ui_component/product_form.xml | 2 +- .../web/css/configurable-product.css | 2 +- .../associated-product-insert-listing.js | 2 +- .../container-configurable-handler.js | 2 +- .../components/custom-options-price-type.js | 2 +- .../js/components/custom-options-warning.js | 2 +- .../components/dynamic-rows-configurable.js | 6 +- .../web/js/components/file-uploader.js | 2 +- .../web/js/components/modal-configurable.js | 2 +- .../web/js/components/price-configurable.js | 4 +- .../web/js/configurable-type-handler.js | 2 +- .../view/adminhtml/web/js/configurable.js | 596 +- .../web/js/options/price-type-handler.js | 5 +- .../web/js/variations/paging/sizes.js | 2 +- .../web/js/variations/product-grid.js | 38 +- .../js/variations/steps/attributes_values.js | 92 +- .../adminhtml/web/js/variations/steps/bulk.js | 129 +- .../js/variations/steps/select_attributes.js | 47 +- .../web/js/variations/steps/summary.js | 45 +- .../adminhtml/web/js/variations/variations.js | 199 +- .../view/adminhtml/web/product/product.css | 2 +- .../web/template/components/actions-list.html | 4 +- .../web/template/components/cell-html.html | 4 +- .../web/template/components/cell-status.html | 4 +- .../template/components/file-uploader.html | 2 +- .../variations/steps/summary-grid.html | 2 +- .../base/layout/catalog_product_prices.xml | 6 +- .../templates/product/price/final_price.phtml | 2 +- .../templates/product/price/tier_price.phtml | 26 + ...catalog_product_view_type_configurable.xml | 2 +- ...ckout_cart_configure_type_configurable.xml | 2 +- .../layout/checkout_cart_item_renderers.xml | 2 +- ...checkout_onepage_review_item_renderers.xml | 2 +- .../view/frontend/requirejs-config.js | 4 +- .../frontend/templates/js/components.phtml | 2 +- .../view/type/options/configurable.phtml | 5 +- .../view/frontend/web/js/configurable.js | 134 +- .../Magento/Contact/Block/ContactForm.php | 2 +- app/code/Magento/Contact/Controller/Index.php | 57 +- .../Contact/Controller/Index/Index.php | 9 +- .../Magento/Contact/Controller/Index/Post.php | 143 +- app/code/Magento/Contact/Helper/Data.php | 6 +- app/code/Magento/Contact/Model/Config.php | 72 + .../Magento/Contact/Model/ConfigInterface.php | 62 + app/code/Magento/Contact/Model/Mail.php | 73 + .../Magento/Contact/Model/MailInterface.php | 23 + .../Model/System/Config/Backend/Links.php | 2 +- .../Test/Unit/Block/ContactFormTest.php | 2 +- .../Test/Unit/Controller/Index/IndexTest.php | 87 +- .../Test/Unit/Controller/Index/PostTest.php | 196 +- .../Test/Unit/Controller/IndexTest.php | 58 +- .../Test/Unit/Controller/Stub/IndexStub.php | 2 +- .../Contact/Test/Unit/Helper/DataTest.php | 2 +- .../Contact/Test/Unit/Model/MailTest.php | 112 + .../Model/System/Config/Backend/LinksTest.php | 2 +- app/code/Magento/Contact/composer.json | 2 +- app/code/Magento/Contact/etc/acl.xml | 2 +- .../Magento/Contact/etc/adminhtml/system.xml | 2 +- app/code/Magento/Contact/etc/config.xml | 2 +- app/code/Magento/Contact/etc/di.xml | 18 + .../Magento/Contact/etc/email_templates.xml | 2 +- app/code/Magento/Contact/etc/frontend/di.xml | 2 +- .../Contact/etc/frontend/page_types.xml | 2 +- .../Magento/Contact/etc/frontend/routes.xml | 4 +- app/code/Magento/Contact/etc/module.xml | 2 +- app/code/Magento/Contact/i18n/en_US.csv | 3 + app/code/Magento/Contact/registration.php | 2 +- .../view/adminhtml/email/submitted_form.html | 2 +- .../frontend/layout/contact_index_index.xml | 2 +- .../Contact/view/frontend/layout/default.xml | 2 +- .../view/frontend/templates/form.phtml | 2 +- .../Magento/Cookie/Block/Html/Notices.php | 2 +- .../Magento/Cookie/Block/RequireCookie.php | 2 +- .../Cookie/Controller/Index/NoCookies.php | 2 +- app/code/Magento/Cookie/Helper/Cookie.php | 2 +- .../Cookie/Model/Config/Backend/Cookie.php | 2 +- .../Cookie/Model/Config/Backend/Domain.php | 2 +- .../Cookie/Model/Config/Backend/Lifetime.php | 2 +- .../Cookie/Model/Config/Backend/Path.php | 2 +- .../Unit/Controller/Index/NoCookiesTest.php | 2 +- .../Cookie/Test/Unit/Helper/CookieTest.php | 2 +- .../Unit/Model/Config/Backend/DomainTest.php | 2 +- .../Model/Config/Backend/LifetimeTest.php | 2 +- .../Unit/Model/Config/Backend/PathTest.php | 2 +- app/code/Magento/Cookie/composer.json | 2 +- .../Magento/Cookie/etc/adminhtml/system.xml | 2 +- app/code/Magento/Cookie/etc/config.xml | 2 +- app/code/Magento/Cookie/etc/di.xml | 2 +- .../Magento/Cookie/etc/frontend/routes.xml | 4 +- app/code/Magento/Cookie/etc/module.xml | 2 +- app/code/Magento/Cookie/registration.php | 2 +- .../Cookie/view/adminhtml/requirejs-config.js | 6 +- .../Cookie/view/frontend/layout/default.xml | 2 +- .../Cookie/view/frontend/requirejs-config.js | 4 +- .../frontend/templates/html/notices.phtml | 2 +- .../frontend/templates/require_cookie.phtml | 4 +- .../Cookie/view/frontend/web/js/notices.js | 24 +- .../view/frontend/web/js/require-cookie.js | 23 +- .../Cron/Console/Command/CronCommand.php | 2 +- .../Console/Command/CronInstallCommand.php | 79 + .../Console/Command/CronRemoveCommand.php | 62 + .../Backend/Config/Structure/Converter.php | 2 +- app/code/Magento/Cron/Model/Config.php | 2 +- .../Model/Config/Backend/Product/Alert.php | 2 +- .../Cron/Model/Config/Backend/Sitemap.php | 2 +- .../Cron/Model/Config/Converter/Db.php | 2 +- .../Cron/Model/Config/Converter/Xml.php | 2 +- app/code/Magento/Cron/Model/Config/Data.php | 25 +- .../Magento/Cron/Model/Config/Reader/Db.php | 17 +- .../Magento/Cron/Model/Config/Reader/Xml.php | 2 +- .../Cron/Model/Config/SchemaLocator.php | 2 +- .../Cron/Model/Config/Source/Frequency.php | 2 +- .../Magento/Cron/Model/ConfigInterface.php | 2 +- .../Model/Groups/Config/Converter/Xml.php | 2 +- .../Magento/Cron/Model/Groups/Config/Data.php | 19 +- .../Cron/Model/Groups/Config/Reader/Xml.php | 2 +- .../Model/Groups/Config/SchemaLocator.php | 2 +- .../Cron/Model/ResourceModel/Schedule.php | 2 +- .../ResourceModel/Schedule/Collection.php | 2 +- app/code/Magento/Cron/Model/Schedule.php | 2 +- .../Model/System/Config/Initial/Converter.php | 2 +- .../Observer/ProcessCronQueueObserver.php | 2 +- app/code/Magento/Cron/Setup/InstallSchema.php | 2 +- .../Unit/Console/Command/CronCommandTest.php | 2 +- .../Command/CronInstallCommandTest.php | 126 + .../Console/Command/CronRemoveCommandTest.php | 72 + .../Unit/Model/Config/Converter/DbTest.php | 2 +- .../Unit/Model/Config/Converter/XmlTest.php | 2 +- .../Cron/Test/Unit/Model/Config/DataTest.php | 21 +- .../Test/Unit/Model/Config/Reader/DbTest.php | 27 +- .../Test/Unit/Model/Config/Reader/XmlTest.php | 2 +- .../Unit/Model/Config/SchemaLocatorTest.php | 2 +- .../Cron/Test/Unit/Model/Config/XsdTest.php | 2 +- .../Model/Config/_files/crontab_invalid.xml | 2 +- .../_files/crontab_invalid_duplicates.xml | 2 +- .../_files/crontab_invalid_node_typo.xml | 2 +- .../crontab_invalid_without_instance.xml | 2 +- .../_files/crontab_invalid_without_method.xml | 2 +- .../_files/crontab_invalid_without_name.xml | 2 +- .../Model/Config/_files/crontab_valid.xml | 2 +- .../_files/crontab_valid_without_schedule.xml | 2 +- .../Cron/Test/Unit/Model/ConfigTest.php | 2 +- .../Cron/Test/Unit/Model/CronJobException.php | 2 +- .../Model/Groups/Config/Converter/XmlTest.php | 2 +- .../Cron/Test/Unit/Model/ScheduleTest.php | 2 +- .../Observer/ProcessCronQueueObserverTest.php | 80 +- app/code/Magento/Cron/composer.json | 2 +- .../Magento/Cron/etc/adminhtml/system.xml | 2 +- app/code/Magento/Cron/etc/cron_groups.xml | 4 +- app/code/Magento/Cron/etc/cron_groups.xsd | 2 +- app/code/Magento/Cron/etc/crontab.xsd | 2 +- app/code/Magento/Cron/etc/crontab/events.xml | 2 +- app/code/Magento/Cron/etc/di.xml | 31 +- app/code/Magento/Cron/etc/module.xml | 2 +- app/code/Magento/Cron/registration.php | 2 +- .../Block/Adminhtml/System/Currency.php | 2 +- .../Adminhtml/System/Currency/Rate/Matrix.php | 2 +- .../System/Currency/Rate/Services.php | 2 +- .../Block/Adminhtml/System/Currencysymbol.php | 2 +- .../Controller/Adminhtml/System/Currency.php | 2 +- .../Adminhtml/System/Currency/FetchRates.php | 2 +- .../Adminhtml/System/Currency/Index.php | 2 +- .../Adminhtml/System/Currency/SaveRates.php | 2 +- .../Adminhtml/System/Currencysymbol.php | 2 +- .../Adminhtml/System/Currencysymbol/Index.php | 2 +- .../Adminhtml/System/Currencysymbol/Save.php | 2 +- .../Model/System/Currencysymbol.php | 20 +- .../Observer/CurrencyDisplayOptions.php | 2 +- .../CurrencySymbol/Setup/UpgradeData.php | 82 + .../System/Currency/Rate/MatrixTest.php | 2 +- .../System/Currency/Rate/ServicesTest.php | 2 +- .../Block/Adminhtml/System/CurrencyTest.php | 2 +- .../Adminhtml/System/CurrencysymbolTest.php | 2 +- .../System/Currencysymbol/IndexTest.php | 2 +- .../System/Currencysymbol/SaveTest.php | 2 +- .../Unit/Model/System/CurrencysymbolTest.php | 179 +- .../Observer/CurrencyDisplayOptionsTest.php | 2 +- app/code/Magento/CurrencySymbol/composer.json | 2 +- app/code/Magento/CurrencySymbol/etc/acl.xml | 2 +- .../CurrencySymbol/etc/adminhtml/menu.xml | 2 +- .../CurrencySymbol/etc/adminhtml/routes.xml | 2 +- app/code/Magento/CurrencySymbol/etc/di.xml | 2 +- .../Magento/CurrencySymbol/etc/events.xml | 2 +- .../Magento/CurrencySymbol/etc/module.xml | 4 +- .../Magento/CurrencySymbol/registration.php | 2 +- .../adminhtml_system_currency_index.xml | 2 +- .../adminhtml_system_currencysymbol_index.xml | 2 +- .../view/adminhtml/templates/grid.phtml | 2 +- .../system/currency/rate/matrix.phtml | 2 +- .../system/currency/rate/services.phtml | 4 +- .../templates/system/currency/rates.phtml | 2 +- .../Api/AccountManagementInterface.php | 2 +- .../Customer/Api/AddressMetadataInterface.php | 2 +- .../AddressMetadataManagementInterface.php | 2 +- .../Api/AddressRepositoryInterface.php | 2 +- .../Api/CustomerManagementInterface.php | 2 +- .../Api/CustomerMetadataInterface.php | 2 +- .../CustomerMetadataManagementInterface.php | 2 +- .../Api/CustomerNameGenerationInterface.php | 2 +- .../Api/CustomerRepositoryInterface.php | 2 +- .../Customer/Api/Data/AddressInterface.php | 2 +- .../Data/AddressSearchResultsInterface.php | 2 +- .../Api/Data/AttributeMetadataInterface.php | 2 +- .../Customer/Api/Data/CustomerInterface.php | 2 +- .../Data/CustomerSearchResultsInterface.php | 2 +- .../Customer/Api/Data/GroupInterface.php | 2 +- .../Api/Data/GroupSearchResultsInterface.php | 2 +- .../Customer/Api/Data/OptionInterface.php | 2 +- .../Customer/Api/Data/RegionInterface.php | 2 +- .../Api/Data/ValidationResultsInterface.php | 2 +- .../Api/Data/ValidationRuleInterface.php | 2 +- .../Customer/Api/GroupManagementInterface.php | 2 +- .../Customer/Api/GroupRepositoryInterface.php | 2 +- .../Customer/Api/MetadataInterface.php | 2 +- .../Api/MetadataManagementInterface.php | 2 +- .../Block/Account/AuthenticationPopup.php | 28 +- .../Block/Account/AuthorizationLink.php | 13 +- .../Customer/Block/Account/Customer.php | 2 +- .../Customer/Block/Account/Dashboard.php | 2 +- .../Block/Account/Dashboard/Address.php | 2 +- .../Customer/Block/Account/Dashboard/Info.php | 2 +- .../Customer/Block/Account/Delimiter.php | 21 + .../Customer/Block/Account/Forgotpassword.php | 2 +- .../Magento/Customer/Block/Account/Link.php | 14 +- .../Customer/Block/Account/Navigation.php | 47 + .../Customer/Block/Account/RegisterLink.php | 2 +- .../Customer/Block/Account/Resetpassword.php | 2 +- .../Customer/Block/Account/SortLink.php | 21 + .../Block/Account/SortLinkInterface.php | 27 + .../Magento/Customer/Block/Address/Book.php | 2 +- .../Magento/Customer/Block/Address/Edit.php | 2 +- .../Address/Renderer/DefaultRenderer.php | 2 +- .../Address/Renderer/RendererInterface.php | 2 +- .../Magento/Customer/Block/Adminhtml/Edit.php | 2 +- .../Block/Adminhtml/Edit/BackButton.php | 2 +- .../Block/Adminhtml/Edit/DeleteButton.php | 2 +- .../Customer/Block/Adminhtml/Edit/Form.php | 2 +- .../Block/Adminhtml/Edit/GenericButton.php | 2 +- .../Adminhtml/Edit/InvalidateTokenButton.php | 2 +- .../Block/Adminhtml/Edit/OrderButton.php | 2 +- .../Block/Adminhtml/Edit/Renderer/Region.php | 2 +- .../Block/Adminhtml/Edit/ResetButton.php | 2 +- .../Adminhtml/Edit/ResetPasswordButton.php | 2 +- .../Adminhtml/Edit/SaveAndContinueButton.php | 2 +- .../Block/Adminhtml/Edit/SaveButton.php | 2 +- .../Block/Adminhtml/Edit/Tab/Cart.php | 2 +- .../Block/Adminhtml/Edit/Tab/Carts.php | 2 +- .../Adminhtml/Edit/Tab/GenericMetadata.php | 2 +- .../Block/Adminhtml/Edit/Tab/Newsletter.php | 2 +- .../Adminhtml/Edit/Tab/Newsletter/Grid.php | 2 +- .../Tab/Newsletter/Grid/Filter/Status.php | 2 +- .../Tab/Newsletter/Grid/Renderer/Action.php | 2 +- .../Tab/Newsletter/Grid/Renderer/Status.php | 2 +- .../Block/Adminhtml/Edit/Tab/Orders.php | 2 +- .../Block/Adminhtml/Edit/Tab/Reviews.php | 2 +- .../Block/Adminhtml/Edit/Tab/View.php | 2 +- .../Block/Adminhtml/Edit/Tab/View/Cart.php | 2 +- .../Edit/Tab/View/Grid/Renderer/Item.php | 2 +- .../Adminhtml/Edit/Tab/View/PersonalInfo.php | 2 +- .../Block/Adminhtml/Edit/Tab/View/Sales.php | 2 +- .../Adminhtml/Edit/Tab/View/Wishlist.php | 2 +- .../Wishlist/Grid/Renderer/Description.php | 2 +- .../Block/Adminhtml/Edit/UnlockButton.php | 2 +- .../Block/Adminhtml/Form/Element/Boolean.php | 2 +- .../Block/Adminhtml/Form/Element/File.php | 2 +- .../Block/Adminhtml/Form/Element/Image.php | 2 +- .../Block/Adminhtml/Grid/Filter/Country.php | 2 +- .../Adminhtml/Grid/Renderer/Multiaction.php | 2 +- .../Customer/Block/Adminhtml/Group.php | 2 +- .../Customer/Block/Adminhtml/Group/Edit.php | 2 +- .../Block/Adminhtml/Group/Edit/Form.php | 2 +- .../Sales/Order/Address/Form/Renderer/Vat.php | 2 +- .../Adminhtml/System/Config/Validatevat.php | 2 +- .../Magento/Customer/Block/CustomerData.php | 2 +- app/code/Magento/Customer/Block/Form/Edit.php | 2 +- .../Magento/Customer/Block/Form/Login.php | 2 +- .../Customer/Block/Form/Login/Info.php | 2 +- .../Magento/Customer/Block/Form/Register.php | 2 +- .../Magento/Customer/Block/Newsletter.php | 2 +- .../Magento/Customer/Block/SectionConfig.php | 2 +- .../Customer/Block/Widget/AbstractWidget.php | 2 +- .../Magento/Customer/Block/Widget/Company.php | 179 + .../Magento/Customer/Block/Widget/Dob.php | 2 +- .../Magento/Customer/Block/Widget/Fax.php | 179 + .../Magento/Customer/Block/Widget/Gender.php | 2 +- .../Magento/Customer/Block/Widget/Name.php | 2 +- .../Magento/Customer/Block/Widget/Taxvat.php | 2 +- .../Customer/Block/Widget/Telephone.php | 179 + .../Command/UpgradeHashAlgorithmCommand.php | 2 +- .../Customer/Controller/AbstractAccount.php | 2 +- .../Customer/Controller/Account/Confirm.php | 2 +- .../Controller/Account/Confirmation.php | 2 +- .../Customer/Controller/Account/Create.php | 2 +- .../Controller/Account/CreatePassword.php | 2 +- .../Controller/Account/CreatePost.php | 2 +- .../Customer/Controller/Account/Edit.php | 2 +- .../Customer/Controller/Account/EditPost.php | 26 +- .../Controller/Account/ForgotPassword.php | 2 +- .../Controller/Account/ForgotPasswordPost.php | 2 +- .../Customer/Controller/Account/Index.php | 2 +- .../Customer/Controller/Account/Login.php | 2 +- .../Customer/Controller/Account/LoginPost.php | 7 +- .../Customer/Controller/Account/Logout.php | 2 +- .../Controller/Account/LogoutSuccess.php | 2 +- .../Controller/Account/ResetPasswordPost.php | 2 +- .../Customer/Controller/AccountInterface.php | 2 +- .../Magento/Customer/Controller/Address.php | 2 +- .../Customer/Controller/Address/Delete.php | 2 +- .../Customer/Controller/Address/Edit.php | 2 +- .../Customer/Controller/Address/Form.php | 2 +- .../Customer/Controller/Address/FormPost.php | 2 +- .../Customer/Controller/Address/Index.php | 2 +- .../Customer/Controller/Address/NewAction.php | 2 +- .../Adminhtml/Cart/Product/Composite/Cart.php | 2 +- .../Cart/Product/Composite/Cart/Configure.php | 2 +- .../Cart/Product/Composite/Cart/Update.php | 2 +- .../Adminhtml/Customer/InvalidateToken.php | 2 +- .../Adminhtml/File/Address/Upload.php | 2 +- .../Adminhtml/File/Customer/Upload.php | 2 +- .../Customer/Controller/Adminhtml/Group.php | 2 +- .../Controller/Adminhtml/Group/Delete.php | 2 +- .../Controller/Adminhtml/Group/Edit.php | 2 +- .../Controller/Adminhtml/Group/Index.php | 2 +- .../Controller/Adminhtml/Group/NewAction.php | 2 +- .../Controller/Adminhtml/Group/Save.php | 2 +- .../Customer/Controller/Adminhtml/Index.php | 2 +- .../Adminhtml/Index/AbstractMassAction.php | 2 +- .../Controller/Adminhtml/Index/Cart.php | 2 +- .../Controller/Adminhtml/Index/Carts.php | 2 +- .../Controller/Adminhtml/Index/Delete.php | 2 +- .../Controller/Adminhtml/Index/Edit.php | 2 +- .../Controller/Adminhtml/Index/Index.php | 2 +- .../Controller/Adminhtml/Index/InlineEdit.php | 2 +- .../Controller/Adminhtml/Index/LastOrders.php | 2 +- .../Adminhtml/Index/MassAssignGroup.php | 2 +- .../Controller/Adminhtml/Index/MassDelete.php | 2 +- .../Adminhtml/Index/MassSubscribe.php | 2 +- .../Adminhtml/Index/MassUnsubscribe.php | 2 +- .../Controller/Adminhtml/Index/NewAction.php | 2 +- .../Controller/Adminhtml/Index/Newsletter.php | 2 +- .../Controller/Adminhtml/Index/Orders.php | 2 +- .../Adminhtml/Index/ProductReviews.php | 2 +- .../Adminhtml/Index/ResetPassword.php | 5 +- .../Controller/Adminhtml/Index/Save.php | 8 +- .../Controller/Adminhtml/Index/Validate.php | 2 +- .../Controller/Adminhtml/Index/ViewCart.php | 2 +- .../Adminhtml/Index/ViewWishlist.php | 2 +- .../Controller/Adminhtml/Index/Viewfile.php | 2 +- .../Controller/Adminhtml/Index/Wishlist.php | 2 +- .../Controller/Adminhtml/Locks/Unlock.php | 2 +- .../Controller/Adminhtml/Online/Index.php | 2 +- .../Adminhtml/System/Config/Validatevat.php | 2 +- .../System/Config/Validatevat/Validate.php | 2 +- .../Config/Validatevat/ValidateAdvanced.php | 2 +- .../Wishlist/Product/Composite/Wishlist.php | 2 +- .../Product/Composite/Wishlist/Configure.php | 2 +- .../Product/Composite/Wishlist/Update.php | 2 +- .../Customer/Controller/Ajax/Login.php | 2 +- .../Customer/Controller/Ajax/Logout.php | 2 +- .../Customer/Controller/Plugin/Account.php | 2 +- .../Customer/Controller/RegistryConstants.php | 2 +- .../Magento/Customer/Controller/Review.php | 2 +- .../Customer/Controller/Review/Index.php | 2 +- .../Customer/Controller/Review/View.php | 2 +- .../Customer/Controller/Section/Load.php | 25 +- .../Customer/CustomerData/Customer.php | 2 +- .../JsLayoutDataProviderInterface.php | 2 +- .../CustomerData/JsLayoutDataProviderPool.php | 2 +- .../JsLayoutDataProviderPoolInterface.php | 2 +- .../CustomerData/Plugin/SessionChecker.php | 2 +- .../Customer/CustomerData/SchemaLocator.php | 2 +- .../CustomerData/Section/Identifier.php | 2 +- .../CustomerData/SectionConfigConverter.php | 2 +- .../Customer/CustomerData/SectionPool.php | 2 +- .../CustomerData/SectionPoolInterface.php | 2 +- .../CustomerData/SectionSourceInterface.php | 2 +- app/code/Magento/Customer/Helper/Address.php | 2 +- .../Helper/Session/CurrentCustomer.php | 2 +- .../Helper/Session/CurrentCustomerAddress.php | 2 +- app/code/Magento/Customer/Helper/View.php | 2 +- .../Customer/Model/Account/Redirect.php | 16 +- .../Customer/Model/AccountManagement.php | 2 +- app/code/Magento/Customer/Model/Address.php | 6 +- .../Model/Address/AbstractAddress.php | 65 +- .../Model/Address/AddressModelInterface.php | 2 +- .../Magento/Customer/Model/Address/Config.php | 24 +- .../Model/Address/Config/Converter.php | 2 +- .../Customer/Model/Address/Config/Reader.php | 2 +- .../Model/Address/Config/SchemaLocator.php | 2 +- .../Model/Address/CustomAttributeList.php | 2 +- .../Address/CustomAttributeListInterface.php | 2 +- .../Magento/Customer/Model/Address/Form.php | 2 +- .../Magento/Customer/Model/Address/Mapper.php | 2 +- .../Model/Address/Validator/Postcode.php | 2 +- .../Customer/Model/AddressRegistry.php | 2 +- .../Model/App/Action/ContextPlugin.php | 2 +- app/code/Magento/Customer/Model/Attribute.php | 32 +- .../Model/Attribute/Backend/Data/Boolean.php | 2 +- .../Model/Attribute/Data/AbstractData.php | 2 +- .../Customer/Model/Attribute/Data/Boolean.php | 2 +- .../Customer/Model/Attribute/Data/Date.php | 2 +- .../Customer/Model/Attribute/Data/File.php | 2 +- .../Customer/Model/Attribute/Data/Hidden.php | 2 +- .../Customer/Model/Attribute/Data/Image.php | 2 +- .../Model/Attribute/Data/Multiline.php | 2 +- .../Model/Attribute/Data/Multiselect.php | 2 +- .../Model/Attribute/Data/Postcode.php | 2 +- .../Customer/Model/Attribute/Data/Select.php | 2 +- .../Customer/Model/Attribute/Data/Text.php | 2 +- .../Model/Attribute/Data/Textarea.php | 2 +- .../Model/AttributeMetadataConverter.php | 2 +- .../Model/AttributeMetadataDataProvider.php | 2 +- .../Magento/Customer/Model/Authentication.php | 2 +- .../Model/AuthenticationInterface.php | 2 +- .../CustomerSessionUserContext.php | 2 +- .../Customer/Model/Backend/Customer.php | 2 +- .../Model/Cache/Type/Notification.php | 2 +- .../Customer/Model/Cart/ConfigPlugin.php | 2 +- .../Model/Checkout/ConfigProvider.php | 2 +- .../Model/Config/Backend/Address/Street.php | 2 +- .../DisableAutoGroupAssignDefault.php | 2 +- .../Password/Link/Expirationperiod.php | 2 +- .../Model/Config/Backend/Show/Address.php | 2 +- .../Model/Config/Backend/Show/AddressOnly.php | 26 + .../Model/Config/Backend/Show/Customer.php | 2 +- .../Magento/Customer/Model/Config/Share.php | 2 +- .../Model/Config/Source/Address/Type.php | 2 +- .../Customer/Model/Config/Source/Group.php | 2 +- .../Model/Config/Source/Group/Multiselect.php | 2 +- app/code/Magento/Customer/Model/Context.php | 2 +- app/code/Magento/Customer/Model/Customer.php | 6 +- .../Customer/Attribute/Backend/Billing.php | 2 +- .../Customer/Attribute/Backend/Password.php | 2 +- .../Customer/Attribute/Backend/Shipping.php | 2 +- .../Customer/Attribute/Backend/Store.php | 2 +- .../Customer/Attribute/Backend/Website.php | 2 +- .../Model/Customer/Attribute/Source/Group.php | 2 +- .../GroupSourceLoggedInOnlyInterface.php | 2 +- .../Model/Customer/Attribute/Source/Store.php | 2 +- .../Customer/Attribute/Source/Website.php | 2 +- .../Customer/Model/Customer/DataProvider.php | 13 +- .../Customer/Model/Customer/Mapper.php | 2 +- .../Model/Customer/NotificationStorage.php | 30 +- .../Customer/Model/Customer/Source/Group.php | 2 +- .../Customer/Source/GroupSourceInterface.php | 2 +- .../Customer/Model/CustomerAuthUpdate.php | 2 +- .../Customer/Model/CustomerExtractor.php | 2 +- .../Customer/Model/CustomerManagement.php | 2 +- .../Customer/Model/CustomerRegistry.php | 2 +- .../Magento/Customer/Model/Data/Address.php | 2 +- .../Customer/Model/Data/AttributeMetadata.php | 2 +- .../Magento/Customer/Model/Data/Customer.php | 2 +- .../Customer/Model/Data/CustomerSecure.php | 2 +- .../Magento/Customer/Model/Data/Group.php | 2 +- .../Magento/Customer/Model/Data/Option.php | 2 +- .../Magento/Customer/Model/Data/Region.php | 2 +- .../Customer/Model/Data/ValidationResults.php | 2 +- .../Customer/Model/Data/ValidationRule.php | 2 +- .../Customer/Model/EmailNotification.php | 2 +- .../Model/EmailNotificationInterface.php | 2 +- .../Magento/Customer/Model/FileProcessor.php | 88 +- .../Magento/Customer/Model/FileUploader.php | 2 +- app/code/Magento/Customer/Model/Form.php | 2 +- app/code/Magento/Customer/Model/Group.php | 2 +- .../Customer/Model/GroupManagement.php | 2 +- .../Magento/Customer/Model/GroupRegistry.php | 2 +- .../Indexer/Address/AttributeProvider.php | 2 +- .../Model/Indexer/Attribute/Filter.php | 2 +- .../Model/Indexer/AttributeProvider.php | 2 +- .../Magento/Customer/Model/Indexer/Source.php | 108 + .../Model/Layout/DepersonalizePlugin.php | 2 +- app/code/Magento/Customer/Model/Log.php | 2 +- app/code/Magento/Customer/Model/Logger.php | 2 +- .../Model/Metadata/AddressCachedMetadata.php | 14 +- .../Model/Metadata/AddressMetadata.php | 2 +- .../Metadata/AddressMetadataManagement.php | 2 +- .../Model/Metadata/AttributeMetadataCache.php | 164 + .../Metadata/AttributeMetadataHydrator.php | 126 + .../Model/Metadata/AttributeResolver.php | 2 +- .../Model/Metadata/CachedMetadata.php | 56 +- .../Model/Metadata/CustomerCachedMetadata.php | 14 +- .../Model/Metadata/CustomerMetadata.php | 2 +- .../Metadata/CustomerMetadataManagement.php | 2 +- .../Model/Metadata/ElementFactory.php | 2 +- .../Magento/Customer/Model/Metadata/Form.php | 2 +- .../Model/Metadata/Form/AbstractData.php | 2 +- .../Customer/Model/Metadata/Form/Boolean.php | 2 +- .../Customer/Model/Metadata/Form/Date.php | 2 +- .../Customer/Model/Metadata/Form/File.php | 2 +- .../Customer/Model/Metadata/Form/Hidden.php | 2 +- .../Customer/Model/Metadata/Form/Image.php | 2 +- .../Model/Metadata/Form/Multiline.php | 2 +- .../Model/Metadata/Form/Multiselect.php | 2 +- .../Customer/Model/Metadata/Form/Postcode.php | 2 +- .../Customer/Model/Metadata/Form/Select.php | 2 +- .../Customer/Model/Metadata/Form/Text.php | 2 +- .../Customer/Model/Metadata/Form/Textarea.php | 2 +- .../Customer/Model/Metadata/FormFactory.php | 2 +- .../Customer/Model/Metadata/Validator.php | 2 +- .../Magento/Customer/Model/Observer/Grid.php | 2 +- app/code/Magento/Customer/Model/Options.php | 2 +- .../Model/Plugin/AllowedCountries.php | 2 +- .../Model/Plugin/CustomerAuthorization.php | 2 +- .../Model/Plugin/CustomerNotification.php | 2 +- .../CustomerRepository/TransactionWrapper.php | 2 +- .../Magento/Customer/Model/Registration.php | 2 +- .../Customer/Model/Renderer/Region.php | 2 +- .../Customer/Model/ResourceModel/Address.php | 2 +- .../Address/Attribute/Backend/Region.php | 2 +- .../Address/Attribute/Collection.php | 2 +- .../Address/Attribute/Source/Country.php | 2 +- .../Attribute/Source/CountryWithWebsites.php | 2 +- .../Address/Attribute/Source/Region.php | 2 +- .../ResourceModel/Address/Collection.php | 2 +- .../ResourceModel/Address/DeleteRelation.php | 2 +- .../Model/ResourceModel/Address/Relation.php | 2 +- .../Model/ResourceModel/AddressRepository.php | 74 +- .../Model/ResourceModel/Attribute.php | 2 +- .../ResourceModel/Attribute/Collection.php | 2 +- .../Customer/Model/ResourceModel/Customer.php | 2 +- .../ResourceModel/Customer/Collection.php | 2 +- .../Model/ResourceModel/Customer/Grid.php | 2 +- .../Customer/Indexer/Collection.php | 20 + .../Model/ResourceModel/Customer/Relation.php | 2 +- .../ResourceModel/CustomerRepository.php | 20 +- .../Db/VersionControl/AddressSnapshot.php | 2 +- .../Model/ResourceModel/Form/Attribute.php | 2 +- .../Form/Attribute/Collection.php | 2 +- .../Model/ResourceModel/Grid/Collection.php | 2 +- .../Customer/Model/ResourceModel/Group.php | 63 +- .../Model/ResourceModel/Group/Collection.php | 2 +- .../ResourceModel/Group/Grid/Collection.php | 2 +- .../Group/Grid/ServiceCollection.php | 2 +- .../Model/ResourceModel/GroupRepository.php | 2 +- .../ResourceModel/Online/Grid/Collection.php | 2 +- .../ResourceModel/Setup/PropertyMapper.php | 2 +- .../Customer/Model/ResourceModel/Visitor.php | 2 +- .../ResourceModel/Visitor/Collection.php | 2 +- app/code/Magento/Customer/Model/Session.php | 2 +- .../Customer/Model/Session/Storage.php | 2 +- app/code/Magento/Customer/Model/Url.php | 41 +- app/code/Magento/Customer/Model/Vat.php | 2 +- app/code/Magento/Customer/Model/Visitor.php | 2 +- .../Observer/AfterAddressSaveObserver.php | 2 +- .../Observer/BeforeAddressSaveObserver.php | 2 +- .../Observer/CustomerLoginSuccessObserver.php | 2 +- .../Observer/LogLastLoginAtObserver.php | 2 +- .../Observer/LogLastLogoutAtObserver.php | 2 +- .../UpgradeCustomerPasswordObserver.php | 2 +- .../Visitor/AbstractVisitorObserver.php | 2 +- .../Visitor/BindCustomerLoginObserver.php | 2 +- .../Visitor/BindCustomerLogoutObserver.php | 2 +- .../Visitor/BindQuoteCreateObserver.php | 2 +- .../Visitor/BindQuoteDestroyObserver.php | 2 +- .../Visitor/InitByRequestObserver.php | 2 +- .../Visitor/SaveByRequestObserver.php | 2 +- .../Magento/Customer/Setup/CustomerSetup.php | 2 +- .../Magento/Customer/Setup/InstallData.php | 2 +- .../Magento/Customer/Setup/InstallSchema.php | 2 +- .../Magento/Customer/Setup/UpgradeData.php | 26 +- .../Magento/Customer/Setup/UpgradeSchema.php | 90 +- .../Block/Account/AuthenticationPopupTest.php | 60 +- .../Block/Account/AuthorizationLinkTest.php | 2 +- .../Test/Unit/Block/Account/CustomerTest.php | 2 +- .../Unit/Block/Account/Dashboard/InfoTest.php | 2 +- .../Test/Unit/Block/Account/LinkTest.php | 2 +- .../Unit/Block/Account/RegisterLinkTest.php | 2 +- .../Unit/Block/Account/ResetpasswordTest.php | 2 +- .../Test/Unit/Block/Address/EditTest.php | 2 +- .../Adminhtml/Edit/Tab/NewsletterTest.php | 2 +- .../Edit/Tab/View/Grid/Renderer/ItemTest.php | 2 +- .../Edit/Tab/View/PersonalInfoTest.php | 2 +- .../Block/Adminhtml/Edit/Tab/ViewTest.php | 2 +- .../Block/Adminhtml/Edit/UnlockButtonTest.php | 2 +- .../Adminhtml/From/Element/ImageTest.php | 2 +- .../Test/Unit/Block/Form/EditTest.php | 2 +- .../Test/Unit/Block/Form/Login/InfoTest.php | 2 +- .../Test/Unit/Block/Form/RegisterTest.php | 2 +- .../Test/Unit/Block/NewsletterTest.php | 2 +- .../Test/Unit/Block/SectionConfigTest.php | 2 +- .../Unit/Block/Widget/AbstractWidgetTest.php | 2 +- .../Test/Unit/Block/Widget/DobTest.php | 2 +- .../Test/Unit/Block/Widget/GenderTest.php | 2 +- .../Test/Unit/Block/Widget/NameTest.php | 2 +- .../Test/Unit/Block/Widget/TaxvatTest.php | 2 +- .../UpgradeHashAlgorithmCommandTest.php | 2 +- .../Unit/Controller/Account/ConfirmTest.php | 2 +- .../Controller/Account/CreatePasswordTest.php | 2 +- .../Controller/Account/CreatePostTest.php | 2 +- .../Unit/Controller/Account/CreateTest.php | 2 +- .../Unit/Controller/Account/EditPostTest.php | 19 +- .../Account/ForgotPasswordPostTest.php | 2 +- .../Unit/Controller/Account/LoginPostTest.php | 9 +- .../Unit/Controller/Account/LogoutTest.php | 2 +- .../Account/ResetPasswordPostTest.php | 2 +- .../Unit/Controller/Address/DeleteTest.php | 2 +- .../Unit/Controller/Address/FormPostTest.php | 2 +- .../Adminhtml/File/Address/UploadTest.php | 2 +- .../Adminhtml/File/Customer/UploadTest.php | 2 +- .../Controller/Adminhtml/Group/SaveTest.php | 2 +- .../Controller/Adminhtml/Index/IndexTest.php | 2 +- .../Adminhtml/Index/InlineEditTest.php | 2 +- .../Adminhtml/Index/MassAssignGroupTest.php | 2 +- .../Adminhtml/Index/MassDeleteTest.php | 2 +- .../Adminhtml/Index/MassSubscribeTest.php | 2 +- .../Adminhtml/Index/MassUnsubscribeTest.php | 2 +- .../Adminhtml/Index/NewsletterTest.php | 2 +- .../Adminhtml/Index/ResetPasswordTest.php | 54 +- .../Controller/Adminhtml/Index/SaveTest.php | 120 +- .../Adminhtml/Index/ValidateTest.php | 2 +- .../Adminhtml/Index/ViewfileTest.php | 2 +- .../Controller/Adminhtml/Locks/UnlockTest.php | 2 +- .../Config/Validatevat/ValidateTest.php | 2 +- .../Test/Unit/Controller/Ajax/LoginTest.php | 2 +- .../Unit/Controller/Plugin/AccountTest.php | 2 +- .../Plugin/SessionCheckerTest.php | 2 +- .../CustomerData/Section/IdentifierTest.php | 2 +- .../SectionConfigConverterTest.php | 2 +- .../Unit/CustomerData/SectionPoolTest.php | 2 +- .../Unit/CustomerData/_files/sections.xml | 2 +- .../Customer/Test/Unit/Helper/AddressTest.php | 2 +- .../Session/CurrentCustomerAddressTest.php | 2 +- .../Helper/Session/CurrentCustomerTest.php | 2 +- .../Customer/Test/Unit/Helper/ViewTest.php | 2 +- .../Test/Unit/Model/Account/RedirectTest.php | 17 +- .../Test/Unit/Model/AccountManagementTest.php | 8 +- .../Model/Address/AbstractAddressTest.php | 35 +- .../Model/Address/Config/ConverterTest.php | 2 +- .../Unit/Model/Address/Config/ReaderTest.php | 2 +- .../Address/Config/SchemaLocatorTest.php | 2 +- .../Unit/Model/Address/Config/XsdTest.php | 2 +- .../Address/Config/_files/formats_merged.php | 2 +- .../Address/Config/_files/formats_merged.xml | 2 +- .../Address/Config/_files/formats_one.xml | 2 +- .../Address/Config/_files/formats_two.xml | 2 +- .../Test/Unit/Model/Address/ConfigTest.php | 112 +- .../Test/Unit/Model/Address/MapperTest.php | 2 +- .../Model/Address/Validator/PostcodeTest.php | 2 +- .../Test/Unit/Model/AddressRegistryTest.php | 2 +- .../Customer/Test/Unit/Model/AddressTest.php | 2 +- .../Model/App/Action/ContextPluginTest.php | 2 +- .../Model/Attribute/Backend/BooleanTest.php | 2 +- .../Model/Attribute/Data/PostcodeTest.php | 2 +- .../Model/AttributeMetadatConverterTest.php | 2 +- .../Test/Unit/Model/AttributeTest.php | 32 +- .../Test/Unit/Model/AuthenticationTest.php | 2 +- .../CustomerSessionUserContextTest.php | 2 +- .../Test/Unit/Model/Backend/CustomerTest.php | 2 +- .../Model/Checkout/ConfigProviderTest.php | 2 +- .../DisableAutoGroupAssignDefaultTest.php | 2 +- .../Model/Config/Source/Address/TypeTest.php | 2 +- .../Config/Source/Group/MultiselectTest.php | 2 +- .../Unit/Model/Config/Source/GroupTest.php | 2 +- .../Attribute/Backend/BillingTest.php | 2 +- .../Attribute/Backend/PasswordTest.php | 2 +- .../Attribute/Backend/ShippingTest.php | 2 +- .../Customer/Attribute/Backend/StoreTest.php | 2 +- .../Attribute/Backend/WebsiteTest.php | 2 +- .../Customer/Attribute/Source/WebsiteTest.php | 2 +- .../Unit/Model/Customer/DataProviderTest.php | 8 +- .../Customer/NotificationStorageTest.php | 60 +- .../Unit/Model/Customer/Source/GroupTest.php | 2 +- .../Unit/Model/CustomerAuthUpdateTest.php | 2 +- .../Test/Unit/Model/CustomerExtractorTest.php | 2 +- .../Unit/Model/CustomerManagementTest.php | 2 +- .../Test/Unit/Model/CustomerRegistryTest.php | 2 +- .../Customer/Test/Unit/Model/CustomerTest.php | 2 +- .../Test/Unit/Model/EmailNotificationTest.php | 2 +- .../Test/Unit/Model/FileProcessorTest.php | 34 +- .../Test/Unit/Model/FileUploaderTest.php | 2 +- .../Test/Unit/Model/GroupRegistryTest.php | 2 +- .../Model/Indexer/Attribute/FilterTest.php | 2 +- .../Model/Indexer/AttributeProviderTest.php | 2 +- .../Model/Layout/DepersonalizePluginTest.php | 2 +- .../Customer/Test/Unit/Model/LogTest.php | 2 +- .../Customer/Test/Unit/Model/LoggerTest.php | 2 +- .../AddressMetadataManagementTest.php | 2 +- .../Model/Metadata/AddressMetadataTest.php | 2 +- .../Metadata/AttributeMetadataCacheTest.php | 226 + .../AttributeMetadataHydratorTest.php | 237 + .../Model/Metadata/AttributeResolverTest.php | 2 +- .../CustomerMetadataManagementTest.php | 2 +- .../Model/Metadata/ElementFactoryTest.php | 2 +- .../Model/Metadata/Form/AbstractDataTest.php | 2 +- .../Metadata/Form/AbstractFormTestCase.php | 2 +- .../Unit/Model/Metadata/Form/BooleanTest.php | 2 +- .../Unit/Model/Metadata/Form/DateTest.php | 2 +- .../Metadata/Form/ExtendsAbstractData.php | 2 +- .../Unit/Model/Metadata/Form/FileTest.php | 2 +- .../Unit/Model/Metadata/Form/HiddenTest.php | 2 +- .../Unit/Model/Metadata/Form/ImageTest.php | 2 +- .../Model/Metadata/Form/MultilineTest.php | 2 +- .../Model/Metadata/Form/MultiselectTest.php | 2 +- .../Unit/Model/Metadata/Form/PostcodeTest.php | 2 +- .../Unit/Model/Metadata/Form/SelectTest.php | 2 +- .../Unit/Model/Metadata/Form/TextTest.php | 2 +- .../Unit/Model/Metadata/Form/TextareaTest.php | 2 +- .../Unit/Model/Metadata/ValidatorTest.php | 2 +- .../Model/Plugin/AllowedCountriesTest.php | 2 +- .../Model/Plugin/CustomerNotificationTest.php | 2 +- .../TransactionWrapperTest.php | 2 +- .../Test/Unit/Model/Renderer/RegionTest.php | 2 +- .../Address/Attribute/Backend/RegionTest.php | 2 +- .../Source/CountryWithWebsitesTest.php | 2 +- .../Address/DeleteRelationTest.php | 2 +- .../ResourceModel/Address/RelationTest.php | 2 +- .../ResourceModel/AddressRepositoryTest.php | 173 +- .../Unit/Model/ResourceModel/AddressTest.php | 2 +- .../Model/ResourceModel/Customer/GridTest.php | 2 +- .../ResourceModel/CustomerRepositoryTest.php | 31 +- .../Db/VersionControl/AddressSnapshotTest.php | 2 +- .../Group/Grid/ServiceCollectionTest.php | 2 +- .../ResourceModel/GroupRepositoryTest.php | 2 +- .../Unit/Model/ResourceModel/GroupTest.php | 75 +- .../Customer/Test/Unit/Model/SessionTest.php | 2 +- .../Customer/Test/Unit/Model/VisitorTest.php | 2 +- .../Observer/AfterAddressSaveObserverTest.php | 2 +- .../BeforeAddressSaveObserverTest.php | 2 +- .../CustomerLoginSuccessObserverTest.php | 2 +- .../Observer/LogLastLoginAtObserverTest.php | 2 +- .../Observer/LogLastLogoutAtObserverTest.php | 2 +- .../UpgradeCustomerPasswordObserverTest.php | 2 +- .../Unit/Ui/Component/ColumnFactoryTest.php | 2 +- .../Component/DataProvider/DocumentTest.php | 2 +- .../Unit/Ui/Component/DataProviderTest.php | 2 +- .../Unit/Ui/Component/FilterFactoryTest.php | 2 +- .../Listing/AttributeRepositoryTest.php | 2 +- .../Listing/Column/AccountLockTest.php | 98 +- .../Component/Listing/Column/ActionsTest.php | 2 +- .../Listing/Column/AttributeColumnTest.php | 2 +- .../Listing/Column/ConfirmationTest.php | 2 +- .../Listing/Column/InlineEditUpdaterTest.php | 2 +- .../Listing/Column/ValidationRulesTest.php | 2 +- .../Unit/Ui/Component/Listing/ColumnsTest.php | 2 +- .../Customer/Ui/Component/ColumnFactory.php | 2 +- .../Customer/Ui/Component/DataProvider.php | 2 +- .../Ui/Component/DataProvider/Document.php | 2 +- .../Customer/Ui/Component/FilterFactory.php | 2 +- .../Component/Listing/AttributeRepository.php | 2 +- .../Component/Listing/Column/AccountLock.php | 12 +- .../Ui/Component/Listing/Column/Actions.php | 2 +- .../Listing/Column/AttributeColumn.php | 2 +- .../Component/Listing/Column/Confirmation.php | 2 +- .../Listing/Column/Group/Options.php | 2 +- .../Listing/Column/InlineEditUpdater.php | 2 +- .../Component/Listing/Column/Online/Type.php | 2 +- .../Listing/Column/Online/Type/Options.php | 2 +- .../Listing/Column/ValidationRules.php | 2 +- .../Ui/Component/Listing/Column/Websites.php | 2 +- .../Customer/Ui/Component/Listing/Columns.php | 2 +- .../Ui/Component/MassAction/Group/Options.php | 2 +- app/code/Magento/Customer/composer.json | 2 +- app/code/Magento/Customer/etc/acl.xml | 2 +- .../Magento/Customer/etc/address_formats.xml | 2 +- .../Magento/Customer/etc/address_formats.xsd | 2 +- .../Magento/Customer/etc/adminhtml/di.xml | 2 +- .../Magento/Customer/etc/adminhtml/menu.xml | 2 +- .../Magento/Customer/etc/adminhtml/routes.xml | 4 +- .../Magento/Customer/etc/adminhtml/system.xml | 17 +- app/code/Magento/Customer/etc/cache.xml | 2 +- app/code/Magento/Customer/etc/config.xml | 19 +- app/code/Magento/Customer/etc/crontab.xml | 2 +- app/code/Magento/Customer/etc/di.xml | 20 +- .../Magento/Customer/etc/email_templates.xml | 2 +- app/code/Magento/Customer/etc/events.xml | 2 +- app/code/Magento/Customer/etc/fieldset.xml | 2 +- app/code/Magento/Customer/etc/frontend/di.xml | 2 +- .../Magento/Customer/etc/frontend/events.xml | 2 +- .../Customer/etc/frontend/page_types.xml | 2 +- .../Magento/Customer/etc/frontend/routes.xml | 4 +- .../Customer/etc/frontend/sections.xml | 3 +- app/code/Magento/Customer/etc/indexer.xml | 4 +- app/code/Magento/Customer/etc/module.xml | 4 +- app/code/Magento/Customer/etc/mview.xml | 2 +- app/code/Magento/Customer/etc/sections.xsd | 2 +- app/code/Magento/Customer/etc/validation.xml | 2 +- app/code/Magento/Customer/etc/webapi.xml | 2 +- .../Magento/Customer/etc/webapi_rest/di.xml | 2 +- .../Magento/Customer/etc/webapi_soap/di.xml | 2 +- app/code/Magento/Customer/i18n/en_US.csv | 5 + app/code/Magento/Customer/registration.php | 2 +- .../adminhtml/layout/customer_group_index.xml | 2 +- .../adminhtml/layout/customer_index_cart.xml | 2 +- .../adminhtml/layout/customer_index_carts.xml | 2 +- .../adminhtml/layout/customer_index_edit.xml | 2 +- .../adminhtml/layout/customer_index_index.xml | 2 +- .../layout/customer_index_newsletter.xml | 2 +- .../layout/customer_index_orders.xml | 2 +- .../layout/customer_index_productreviews.xml | 2 +- .../layout/customer_index_viewcart.xml | 2 +- .../layout/customer_index_viewwishlist.xml | 2 +- .../layout/customer_online_index.xml | 2 +- .../view/adminhtml/requirejs-config.js | 8 +- .../view/adminhtml/templates/edit/js.phtml | 2 +- .../create/address/form/renderer/vat.phtml | 2 +- .../templates/system/config/validatevat.phtml | 2 +- .../view/adminhtml/templates/tab/cart.phtml | 2 +- .../adminhtml/templates/tab/newsletter.phtml | 2 +- .../view/adminhtml/templates/tab/view.phtml | 2 +- .../templates/tab/view/personal_info.phtml | 2 +- .../adminhtml/templates/tab/view/sales.phtml | 2 +- .../ui_component/customer_listing.xml | 2 +- .../ui_component/customer_online_grid.xml | 2 +- .../view/adminhtml/web/edit/post-wrapper.js | 2 +- .../adminhtml/web/edit/tab/js/addresses.js | 94 +- .../web/js/bootstrap/customer-post-action.js | 2 +- .../view/base/ui_component/customer_form.xml | 2 +- .../view/frontend/email/account_new.html | 2 +- .../email/account_new_confirmation.html | 2 +- .../frontend/email/account_new_confirmed.html | 2 +- .../email/account_new_no_password.html | 2 +- .../view/frontend/email/change_email.html | 2 +- .../email/change_email_and_password.html | 2 +- .../view/frontend/email/password_new.html | 2 +- .../view/frontend/email/password_reset.html | 2 +- .../email/password_reset_confirmation.html | 2 +- .../view/frontend/layout/customer_account.xml | 55 +- .../layout/customer_account_confirmation.xml | 2 +- .../layout/customer_account_create.xml | 2 +- .../customer_account_createpassword.xml | 2 +- .../frontend/layout/customer_account_edit.xml | 2 +- .../customer_account_forgotpassword.xml | 2 +- .../layout/customer_account_index.xml | 2 +- .../layout/customer_account_login.xml | 2 +- .../layout/customer_account_logoutsuccess.xml | 2 +- .../frontend/layout/customer_address_form.xml | 2 +- .../layout/customer_address_index.xml | 2 +- .../Customer/view/frontend/layout/default.xml | 3 +- .../view/frontend/requirejs-config.js | 5 +- .../account/authentication-popup.phtml | 4 +- .../frontend/templates/account/customer.phtml | 2 +- .../templates/account/dashboard/address.phtml | 2 +- .../templates/account/dashboard/info.phtml | 2 +- .../account/link/authorization.phtml | 2 +- .../templates/account/link/back.phtml | 2 +- .../account/navigation-delimiter.phtml | 9 + .../templates/account/navigation.phtml | 2 +- .../templates/additionalinfocustomer.phtml | 2 +- .../frontend/templates/address/book.phtml | 2 +- .../frontend/templates/address/edit.phtml | 55 +- .../templates/form/confirmation.phtml | 2 +- .../view/frontend/templates/form/edit.phtml | 4 +- .../templates/form/forgotpassword.phtml | 2 +- .../view/frontend/templates/form/login.phtml | 2 +- .../frontend/templates/form/newsletter.phtml | 2 +- .../frontend/templates/form/register.phtml | 31 +- .../form/resetforgottenpassword.phtml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../frontend/templates/js/customer-data.phtml | 2 +- .../templates/js/section-config.phtml | 2 +- .../view/frontend/templates/logout.phtml | 2 +- .../view/frontend/templates/newcustomer.phtml | 2 +- .../frontend/templates/widget/company.phtml | 32 + .../view/frontend/templates/widget/dob.phtml | 2 +- .../view/frontend/templates/widget/fax.phtml | 33 + .../frontend/templates/widget/gender.phtml | 2 +- .../view/frontend/templates/widget/name.phtml | 2 +- .../frontend/templates/widget/taxvat.phtml | 2 +- .../frontend/templates/widget/telephone.phtml | 33 + .../Customer/view/frontend/web/address.js | 50 +- .../frontend/web/change-email-password.js | 2 +- .../web/js/action/check-email-availability.js | 56 +- .../view/frontend/web/js/action/login.js | 106 +- .../view/frontend/web/js/addressValidation.js | 43 + .../view/frontend/web/js/checkout-balance.js | 22 +- .../view/frontend/web/js/customer-data.js | 20 +- .../frontend/web/js/model/address-list.js | 22 +- .../web/js/model/authentication-popup.js | 65 +- .../web/js/model/customer-addresses.js | 52 +- .../view/frontend/web/js/model/customer.js | 230 +- .../frontend/web/js/model/customer/address.js | 95 +- .../web/js/password-strength-indicator.js | 2 +- .../view/frontend/web/js/section-config.js | 70 +- .../web/js/view/authentication-popup.js | 154 +- .../view/frontend/web/js/view/customer.js | 4 +- .../web/template/authentication-popup.html | 5 +- .../Magento/CustomerAnalytics/LICENSE.txt | 48 + .../Magento/CustomerAnalytics/LICENSE_AFL.txt | 48 + app/code/Magento/CustomerAnalytics/README.md | 4 + .../Magento/CustomerAnalytics/composer.json | 26 + .../CustomerAnalytics/etc/analytics.xml | 18 + .../Magento/CustomerAnalytics/etc/module.xml | 15 + .../Magento/CustomerAnalytics/etc/reports.xml | 37 + .../CustomerAnalytics/registration.php | 11 + .../Controller/Adminhtml/Index/ExportCsv.php | 2 +- .../Controller/Adminhtml/Index/ExportXml.php | 2 +- .../Model/Export/Address.php | 2 +- .../Model/Export/Customer.php | 2 +- .../Model/Import/AbstractCustomer.php | 2 +- .../Model/Import/Address.php | 2 +- .../Model/Import/Customer.php | 6 +- .../Model/Import/CustomerComposite.php | 2 +- .../ResourceModel/Import/Customer/Storage.php | 2 +- .../Import/CustomerComposite/Data.php | 2 +- .../Test/Unit/Model/Export/AddressTest.php | 2 +- .../Test/Unit/Model/Export/CustomerTest.php | 2 +- .../Model/Import/AbstractCustomerTest.php | 2 +- .../Test/Unit/Model/Import/AddressTest.php | 2 +- .../Model/Import/CustomerCompositeTest.php | 2 +- .../Test/Unit/Model/Import/CustomerTest.php | 2 +- .../_files/row_data_abstract_empty_email.php | 2 +- .../row_data_abstract_empty_website.php | 2 +- .../row_data_abstract_invalid_email.php | 2 +- .../row_data_abstract_invalid_website.php | 2 +- .../_files/row_data_abstract_no_email.php | 2 +- .../_files/row_data_abstract_no_website.php | 2 +- .../Import/_files/row_data_abstract_valid.php | 2 +- ..._data_address_delete_address_not_found.php | 2 +- ...w_data_address_delete_empty_address_id.php | 2 +- .../row_data_address_delete_no_customer.php | 2 +- .../_files/row_data_address_delete_valid.php | 2 +- ...dress_update_absent_required_attribute.php | 2 +- ...w_data_address_update_empty_address_id.php | 2 +- ...row_data_address_update_invalid_region.php | 2 +- .../row_data_address_update_no_customer.php | 2 +- .../_files/row_data_address_update_valid.php | 2 +- .../Import/Customer/StorageTest.php | 2 +- .../Import/CustomerComposite/DataTest.php | 2 +- .../CustomerImportExport/composer.json | 2 +- .../etc/adminhtml/routes.xml | 4 +- .../CustomerImportExport/etc/config.xml | 2 +- .../CustomerImportExport/etc/export.xml | 2 +- .../CustomerImportExport/etc/import.xml | 2 +- .../CustomerImportExport/etc/module.xml | 2 +- .../CustomerImportExport/registration.php | 2 +- ...customer_import_export_index_exportcsv.xml | 2 +- ...customer_import_export_index_exportxml.xml | 2 +- .../layout/customer_index_grid_block.xml | 2 +- .../Command/App/ApplicationDumpCommand.php | 92 + .../SensitiveConfigSet/CollectorFactory.php | 74 + .../SensitiveConfigSet/CollectorInterface.php | 35 + .../InteractiveCollector.php | 67 + .../SensitiveConfigSet/SimpleCollector.php | 131 + .../Command/App/SensitiveConfigSetCommand.php | 196 + .../Command/DeployStaticOptionsInterface.php | 2 +- .../Deploy/Console/Command/SetModeCommand.php | 2 +- .../Console/Command/ShowModeCommand.php | 2 +- .../Magento/Deploy/Model/ConfigWriter.php | 109 + .../Deploy/Model/Deploy/DeployInterface.php | 2 +- .../Model/Deploy/JsDictionaryDeploy.php | 89 + .../Deploy/Model/Deploy/LocaleDeploy.php | 28 +- .../Deploy/Model/Deploy/LocaleQuickDeploy.php | 96 +- .../Deploy/Model/Deploy/TemplateMinifier.php | 2 +- .../Magento/Deploy/Model/DeployManager.php | 2 +- .../Deploy/Model/DeployStrategyFactory.php | 8 +- .../Deploy/Model/DeployStrategyProvider.php | 2 +- app/code/Magento/Deploy/Model/Deployer.php | 2 +- app/code/Magento/Deploy/Model/Filesystem.php | 64 +- app/code/Magento/Deploy/Model/Mode.php | 6 +- app/code/Magento/Deploy/Model/Process.php | 2 +- .../Magento/Deploy/Model/ProcessManager.php | 2 +- .../Deploy/Model/ProcessQueueManager.php | 2 +- app/code/Magento/Deploy/Model/ProcessTask.php | 2 +- .../CollectorFactoryTest.php | 84 + .../InteractiveCollectorTest.php | 103 + .../SimpleCollectorTest.php | 190 + .../App/SensitiveConfigSetCommandTest.php | 265 + .../Command/ApplicationDumpCommandTest.php | 91 + .../Console/Command/SetModeCommandTest.php | 2 +- .../Console/Command/ShowModeCommandTest.php | 2 +- .../Test/Unit/Model/ConfigWriterTest.php | 96 + .../Model/Deploy/JsDictionaryDeployTest.php | 103 + .../Unit/Model/Deploy/LocaleDeployTest.php | 256 +- .../Model/Deploy/LocaleQuickDeployTest.php | 82 +- .../Model/Deploy/TemplateMinifierTest.php | 2 +- .../Test/Unit/Model/DeployManagerTest.php | 2 +- .../Unit/Model/DeployStrategyFactoryTest.php | 2 +- .../Deploy/Test/Unit/Model/FilesystemTest.php | 2 +- .../Unit/Model/ProcessQueueManagerTest.php | 2 +- app/code/Magento/Deploy/composer.json | 5 +- app/code/Magento/Deploy/etc/di.xml | 13 +- app/code/Magento/Deploy/etc/module.xml | 2 +- app/code/Magento/Deploy/registration.php | 2 +- .../Adminhtml/System/Config/WorkflowType.php | 2 +- .../Console/Command/DevTestsRunCommand.php | 2 +- .../Command/SourceThemeDeployCommand.php | 2 +- .../Command/XmlCatalogGenerateCommand.php | 2 +- .../Console/Command/XmlConverterCommand.php | 2 +- app/code/Magento/Developer/Helper/Data.php | 2 +- .../Model/Config/Backend/AllowedIps.php | 2 +- .../Model/Config/Backend/WorkflowType.php | 2 +- .../Model/Config/Source/WorkflowType.php | 2 +- .../FileGenerator/PublicationDecorator.php | 2 +- .../Developer/Model/Logger/Handler/Debug.php | 25 +- .../TemplateEngine/Decorator/DebugHints.php | 2 +- .../TemplateEngine/Plugin/DebugHints.php | 2 +- .../Developer/Model/Tools/Formatter.php | 2 +- .../PreProcessor/FrontendCompilation.php | 14 +- .../PreProcessor/PreprocessorStrategy.php | 2 +- .../ClientSideLessCompilation/Renderer.php | 2 +- .../View/Page/Config/RendererFactory.php | 2 +- .../XmlCatalog/Format/FormatInterface.php | 2 +- .../Model/XmlCatalog/Format/PhpStorm.php | 2 +- .../System/Config/WorkflowTypeTest.php | 2 +- .../Command/DevTestsRunCommandTest.php | 2 +- .../Command/SourceThemeDeployCommandTest.php | 2 +- .../Command/XmlCatalogGenerateCommandTest.php | 2 +- .../Command/XmlConverterCommandTest.php | 2 +- .../Test/Unit/Console/Command/_files/test.xml | 2 +- .../Developer/Test/Unit/Helper/DataTest.php | 2 +- .../Model/Config/Backend/AllowedIpsTest.php | 2 +- .../Model/Config/Backend/WorkflowTypeTest.php | 2 +- .../Model/Config/Source/WorkflowTypeTest.php | 2 +- .../PublicationDecoratorTest.php | 2 +- .../Unit/Model/Logger/Handler/DebugTest.php | 47 +- .../Decorator/DebugHintsTest.php | 2 +- .../TemplateEngine/Plugin/DebugHintsTest.php | 2 +- .../PreProcessor/FrontendCompilationTest.php | 34 +- .../PreProcessor/PreprocessorStrategyTest.php | 2 +- .../RendererTest.php | 2 +- .../View/Page/Config/RendererFactoryTest.php | 2 +- app/code/Magento/Developer/composer.json | 2 +- .../Magento/Developer/etc/adminhtml/di.xml | 2 +- .../Developer/etc/adminhtml/system.xml | 2 +- app/code/Magento/Developer/etc/config.xml | 2 +- app/code/Magento/Developer/etc/di.xml | 9 +- .../Magento/Developer/etc/frontend/di.xml | 2 +- app/code/Magento/Developer/etc/module.xml | 2 +- app/code/Magento/Developer/registration.php | 2 +- .../Dhl/Block/Adminhtml/Unitofmeasure.php | 2 +- app/code/Magento/Dhl/Model/AbstractDhl.php | 2 +- app/code/Magento/Dhl/Model/Carrier.php | 2 +- .../Plugin/Checkout/Block/Cart/Shipping.php | 2 +- .../Magento/Dhl/Model/Source/Contenttype.php | 2 +- .../Model/Source/Method/AbstractMethod.php | 2 +- .../Magento/Dhl/Model/Source/Method/Doc.php | 2 +- .../Dhl/Model/Source/Method/Freedoc.php | 2 +- .../Dhl/Model/Source/Method/Freenondoc.php | 2 +- .../Dhl/Model/Source/Method/Generic.php | 2 +- .../Dhl/Model/Source/Method/Nondoc.php | 2 +- .../Magento/Dhl/Model/Source/Method/Size.php | 2 +- .../Dhl/Model/Source/Method/Unitofmeasure.php | 2 +- app/code/Magento/Dhl/Setup/InstallData.php | 2 +- .../Dhl/Test/Unit/Model/CarrierTest.php | 2 +- .../Dhl/Test/Unit/Model/_files/countries.xml | 4 +- .../Model/_files/rates_request_data_dhl.php | 2 +- .../Model/_files/response_shipping_label.xml | 2 +- .../_files/success_dhl_response_rates.xml | 2 +- app/code/Magento/Dhl/composer.json | 2 +- app/code/Magento/Dhl/etc/adminhtml/system.xml | 2 +- app/code/Magento/Dhl/etc/config.xml | 2 +- app/code/Magento/Dhl/etc/countries.xml | 2 +- app/code/Magento/Dhl/etc/di.xml | 11 +- app/code/Magento/Dhl/etc/module.xml | 2 +- app/code/Magento/Dhl/registration.php | 2 +- .../adminhtml/templates/unitofmeasure.phtml | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../model/shipping-rates-validation-rules.js | 46 +- .../web/js/model/shipping-rates-validator.js | 59 +- .../web/js/view/shipping-rates-validation.js | 45 +- .../CountryInformationAcquirerInterface.php | 2 +- .../CurrencyInformationAcquirerInterface.php | 2 +- .../Api/Data/CountryInformationInterface.php | 2 +- .../Api/Data/CurrencyInformationInterface.php | 2 +- .../Api/Data/ExchangeRateInterface.php | 2 +- .../Api/Data/RegionInformationInterface.php | 2 +- .../Adminhtml/Frontend/Currency/Base.php | 2 +- .../Adminhtml/Frontend/Region/Updater.php | 2 +- app/code/Magento/Directory/Block/Currency.php | 2 +- app/code/Magento/Directory/Block/Data.php | 33 +- .../Adminhtml/Json/CountryRegion.php | 2 +- .../Controller/Currency/SwitchAction.php | 2 +- app/code/Magento/Directory/Helper/Data.php | 2 +- .../Directory/Model/AllowedCountries.php | 2 +- .../Model/Config/Source/Allregion.php | 2 +- .../Directory/Model/Config/Source/Country.php | 2 +- .../Model/Config/Source/Country/Full.php | 2 +- .../Model/Config/Source/WeightUnit.php | 2 +- app/code/Magento/Directory/Model/Country.php | 2 +- .../Directory/Model/Country/Format.php | 2 +- .../Model/Country/Postcode/Config.php | 2 +- .../Country/Postcode/Config/Converter.php | 2 +- .../Model/Country/Postcode/Config/Data.php | 19 +- .../Model/Country/Postcode/Config/Reader.php | 7 +- .../Country/Postcode/Config/SchemaLocator.php | 2 +- .../Country/Postcode/ConfigInterface.php | 2 +- .../Model/Country/Postcode/Validator.php | 2 +- .../Country/Postcode/ValidatorInterface.php | 2 +- .../Directory/Model/CountryFactory.php | 2 +- .../Model/CountryInformationAcquirer.php | 2 +- app/code/Magento/Directory/Model/Currency.php | 2 +- .../Model/Currency/DefaultLocator.php | 2 +- .../Directory/Model/Currency/Filter.php | 2 +- .../Model/Currency/Import/AbstractImport.php | 2 +- .../Model/Currency/Import/Config.php | 2 +- .../Model/Currency/Import/Factory.php | 2 +- .../Model/Currency/Import/FixerIo.php | 2 +- .../Model/Currency/Import/ImportInterface.php | 2 +- .../Model/Currency/Import/Source/Service.php | 2 +- .../Model/Currency/Import/Webservicex.php | 2 +- .../Model/Currency/Import/YahooFinance.php | 2 +- .../Model/CurrencyInformationAcquirer.php | 2 +- .../Model/Data/CountryInformation.php | 2 +- .../Model/Data/CurrencyInformation.php | 2 +- .../Directory/Model/Data/ExchangeRate.php | 2 +- .../Model/Data/RegionInformation.php | 2 +- app/code/Magento/Directory/Model/Observer.php | 2 +- .../Magento/Directory/Model/PriceCurrency.php | 2 +- app/code/Magento/Directory/Model/Region.php | 2 +- .../Magento/Directory/Model/RegionFactory.php | 2 +- .../Directory/Model/ResourceModel/Country.php | 2 +- .../ResourceModel/Country/Collection.php | 2 +- .../Model/ResourceModel/Country/Format.php | 2 +- .../Country/Format/Collection.php | 2 +- .../Model/ResourceModel/Currency.php | 2 +- .../Directory/Model/ResourceModel/Region.php | 2 +- .../Model/ResourceModel/Region/Collection.php | 51 +- .../Magento/Directory/Setup/InstallData.php | 2 +- .../Magento/Directory/Setup/InstallSchema.php | 2 +- .../Test/Unit/Block/CurrencyTest.php | 2 +- .../Directory/Test/Unit/Block/DataTest.php | 130 +- .../Directory/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Model/AllowedCountriesTest.php | 2 +- .../Model/Config/Source/AllRegionTest.php | 2 +- .../Unit/Model/Config/Source/CountryTest.php | 2 +- .../Country/Postcode/Config/ConverterTest.php | 2 +- .../Country/Postcode/Config/DataTest.php | 51 +- .../Country/Postcode/Config/ReaderTest.php | 2 +- .../Postcode/Config/SchemaLocatorTest.php | 2 +- .../Model/Country/Postcode/ConfigTest.php | 2 +- .../Model/Country/Postcode/ValidatorTest.php | 2 +- .../Model/CountryInformationAcquirerTest.php | 2 +- .../Directory/Test/Unit/Model/CountryTest.php | 2 +- .../Model/Currency/DefaultLocatorTest.php | 2 +- .../Unit/Model/Currency/Import/ConfigTest.php | 2 +- .../Model/Currency/Import/FactoryTest.php | 2 +- .../Model/Currency/Import/FixerIoTest.php | 2 +- .../Currency/Import/Source/ServiceTest.php | 2 +- .../Currency/Import/YahooFinanceTest.php | 2 +- .../Model/CurrencyInformationAcquirerTest.php | 2 +- .../Test/Unit/Model/CurrencyTest.php | 2 +- .../Test/Unit/Model/ObserverTest.php | 2 +- .../Test/Unit/Model/PriceCurrencyTest.php | 2 +- .../ResourceModel/Country/CollectionTest.php | 2 +- .../ResourceModel/Region/CollectionTest.php | 36 +- .../Directory/Test/Unit/_files/zip_codes.php | 2 +- .../Directory/Test/Unit/_files/zip_codes.xml | 2 +- app/code/Magento/Directory/composer.json | 2 +- .../Magento/Directory/etc/adminhtml/di.xml | 2 +- .../Directory/etc/adminhtml/routes.xml | 2 +- .../Directory/etc/adminhtml/system.xml | 2 +- app/code/Magento/Directory/etc/config.xml | 2 +- app/code/Magento/Directory/etc/crontab.xml | 2 +- app/code/Magento/Directory/etc/di.xml | 10 +- .../Magento/Directory/etc/email_templates.xml | 2 +- .../Magento/Directory/etc/frontend/routes.xml | 4 +- .../Directory/etc/frontend/sections.xml | 2 +- app/code/Magento/Directory/etc/module.xml | 2 +- app/code/Magento/Directory/etc/webapi.xml | 2 +- app/code/Magento/Directory/etc/zip_codes.xml | 2 +- app/code/Magento/Directory/etc/zip_codes.xsd | 4 +- app/code/Magento/Directory/registration.php | 2 +- .../email/currency_update_notification.html | 2 +- .../templates/js/optional_zip_countries.phtml | 2 +- .../frontend/layout/catalog_category_view.xml | 2 +- .../layout/catalogsearch_advanced_index.xml | 2 +- .../layout/catalogsearch_advanced_result.xml | 2 +- .../layout/catalogsearch_result_index.xml | 2 +- .../view/frontend/layout/default.xml | 2 +- .../view/frontend/templates/currency.phtml | 2 +- .../frontend/templates/currency/switch.phtml | 2 +- .../Api/Data/DownloadableOptionInterface.php | 2 +- .../Api/Data/File/ContentInterface.php | 2 +- .../Data/File/ContentUploaderInterface.php | 2 +- .../Downloadable/Api/Data/LinkInterface.php | 2 +- .../Api/Data/ProductAttributeInterface.php | 2 +- .../Downloadable/Api/Data/SampleInterface.php | 2 +- .../Api/LinkRepositoryInterface.php | 2 +- .../Api/SampleRepositoryInterface.php | 2 +- .../Composite/Fieldset/Downloadable.php | 2 +- .../Catalog/Product/Edit/Tab/Downloadable.php | 2 +- .../Product/Edit/Tab/Downloadable/Links.php | 2 +- .../Product/Edit/Tab/Downloadable/Samples.php | 2 +- .../Sales/Items/Column/Downloadable/Name.php | 2 +- .../Block/Catalog/Product/Links.php | 2 +- .../Block/Catalog/Product/Samples.php | 2 +- .../Block/Catalog/Product/View/Type.php | 2 +- .../Block/Checkout/Cart/Item/Renderer.php | 2 +- .../Downloadable/Block/Checkout/Success.php | 2 +- .../Block/Customer/Products/ListProducts.php | 2 +- .../Sales/Order/Email/Items/Downloadable.php | 2 +- .../Order/Email/Items/Order/Downloadable.php | 2 +- .../Order/Item/Renderer/Downloadable.php | 2 +- .../Adminhtml/Downloadable/File.php | 2 +- .../Adminhtml/Downloadable/File/Upload.php | 4 +- .../Product/Edit/AddAttributeToTemplate.php | 2 +- .../Product/Edit/AlertsPriceGrid.php | 2 +- .../Product/Edit/AlertsStockGrid.php | 2 +- .../Downloadable/Product/Edit/Categories.php | 2 +- .../Downloadable/Product/Edit/Crosssell.php | 2 +- .../Product/Edit/CrosssellGrid.php | 2 +- .../Product/Edit/CustomOptions.php | 2 +- .../Downloadable/Product/Edit/Duplicate.php | 2 +- .../Downloadable/Product/Edit/Edit.php | 2 +- .../Downloadable/Product/Edit/Form.php | 2 +- .../Downloadable/Product/Edit/Grid.php | 2 +- .../Downloadable/Product/Edit/GridOnly.php | 2 +- .../Downloadable/Product/Edit/Index.php | 2 +- .../Downloadable/Product/Edit/Link.php | 2 +- .../Downloadable/Product/Edit/MassDelete.php | 2 +- .../Downloadable/Product/Edit/MassStatus.php | 2 +- .../Downloadable/Product/Edit/NewAction.php | 2 +- .../Downloadable/Product/Edit/Options.php | 2 +- .../Product/Edit/OptionsImportGrid.php | 2 +- .../Downloadable/Product/Edit/Related.php | 2 +- .../Downloadable/Product/Edit/RelatedGrid.php | 2 +- .../Downloadable/Product/Edit/Sample.php | 2 +- .../Downloadable/Product/Edit/Save.php | 2 +- .../Product/Edit/ShowUpdateResult.php | 2 +- .../Product/Edit/SuggestAttributes.php | 2 +- .../Downloadable/Product/Edit/Upsell.php | 2 +- .../Downloadable/Product/Edit/UpsellGrid.php | 2 +- .../Downloadable/Product/Edit/Validate.php | 2 +- .../Downloadable/Product/Edit/Wysiwyg.php | 2 +- .../Helper/Plugin/Downloadable.php | 2 +- .../Controller/Customer/Products.php | 2 +- .../Downloadable/Controller/Download.php | 2 +- .../Downloadable/Controller/Download/Link.php | 2 +- .../Controller/Download/LinkSample.php | 2 +- .../Controller/Download/Sample.php | 2 +- .../Helper/Catalog/Product/Configuration.php | 2 +- app/code/Magento/Downloadable/Helper/Data.php | 2 +- .../Magento/Downloadable/Helper/Download.php | 2 +- app/code/Magento/Downloadable/Helper/File.php | 2 +- .../Downloadable/Model/ComponentInterface.php | 2 +- .../Downloadable/Model/DownloadableOption.php | 2 +- .../Downloadable/Model/File/Content.php | 2 +- .../Model/File/ContentUploader.php | 2 +- .../Model/File/ContentValidator.php | 2 +- app/code/Magento/Downloadable/Model/Link.php | 2 +- .../Downloadable/Model/Link/Builder.php | 2 +- .../Model/Link/ContentValidator.php | 2 +- .../Downloadable/Model/Link/CreateHandler.php | 2 +- .../Downloadable/Model/Link/DeleteHandler.php | 2 +- .../Downloadable/Model/Link/Purchased.php | 2 +- .../Model/Link/Purchased/Item.php | 2 +- .../Downloadable/Model/Link/ReadHandler.php | 2 +- .../Downloadable/Model/Link/UpdateHandler.php | 2 +- .../Downloadable/Model/LinkRepository.php | 2 +- .../CartConfiguration/Plugin/Downloadable.php | 2 +- .../Product/CopyConstructor/Downloadable.php | 2 +- .../Downloadable/Model/Product/Price.php | 2 +- .../Downloadable/Model/Product/Type.php | 16 +- .../TypeHandler/AbstractTypeHandler.php | 2 +- .../Model/Product/TypeHandler/Link.php | 2 +- .../Model/Product/TypeHandler/Sample.php | 2 +- .../Model/Product/TypeHandler/TypeHandler.php | 2 +- .../TypeHandler/TypeHandlerInterface.php | 2 +- .../Plugin/Downloadable.php | 2 +- .../Model/ProductOptionProcessor.php | 2 +- .../Model/Quote/Item/CartItemProcessor.php | 2 +- .../Model/ResourceModel/Indexer/Price.php | 2 +- .../Downloadable/Model/ResourceModel/Link.php | 2 +- .../Model/ResourceModel/Link/Collection.php | 2 +- .../Model/ResourceModel/Link/Purchased.php | 2 +- .../Link/Purchased/Collection.php | 2 +- .../ResourceModel/Link/Purchased/Item.php | 2 +- .../Link/Purchased/Item/Collection.php | 2 +- .../Model/ResourceModel/Sample.php | 2 +- .../Model/ResourceModel/Sample/Collection.php | 2 +- .../Sales/Order/Pdf/Items/AbstractItems.php | 2 +- .../Sales/Order/Pdf/Items/Creditmemo.php | 2 +- .../Model/Sales/Order/Pdf/Items/Invoice.php | 2 +- .../Magento/Downloadable/Model/Sample.php | 2 +- .../Downloadable/Model/Sample/Builder.php | 2 +- .../Model/Sample/ContentValidator.php | 2 +- .../Model/Sample/CreateHandler.php | 2 +- .../Model/Sample/DeleteHandler.php | 2 +- .../Downloadable/Model/Sample/ReadHandler.php | 2 +- .../Model/Sample/UpdateHandler.php | 2 +- .../Downloadable/Model/SampleRepository.php | 2 +- .../Downloadable/Model/Source/Shareable.php | 2 +- .../Downloadable/Model/Source/TypeUpload.php | 2 +- .../Config/Source/Contentdisposition.php | 2 +- .../System/Config/Source/Orderitemstatus.php | 2 +- .../Observer/InitOptionRendererObserver.php | 2 +- .../IsAllowedGuestCheckoutObserver.php | 2 +- .../SaveDownloadableOrderItemObserver.php | 2 +- .../SetHasDownloadableProductsObserver.php | 2 +- .../Observer/SetLinkStatusObserver.php | 2 +- .../Downloadable/Pricing/Price/LinkPrice.php | 2 +- .../Pricing/Price/LinkPriceInterface.php | 2 +- .../Downloadable/Setup/InstallData.php | 2 +- .../Downloadable/Setup/InstallSchema.php | 2 +- .../Downloadable/Setup/UpgradeSchema.php | 41 + .../Edit/Tab/Downloadable/LinksTest.php | 2 +- .../Edit/Tab/Downloadable/SamplesTest.php | 2 +- .../Items/Column/Downloadable/NameTest.php | 2 +- .../Unit/Block/Catalog/Product/LinksTest.php | 2 +- .../Order/Email/Items/DownloadableTest.php | 2 +- .../Email/Items/Order/DownloadableTest.php | 2 +- .../Order/Item/Renderer/DownloadableTest.php | 2 +- .../Downloadable/File/UploadTest.php | 2 +- .../Downloadable/Product/Edit/LinkTest.php | 2 +- .../Downloadable/Product/Edit/SampleTest.php | 2 +- .../Helper/Plugin/DownloadableTest.php | 2 +- .../Controller/Download/LinkSampleTest.php | 2 +- .../Unit/Controller/Download/LinkTest.php | 2 +- .../Unit/Controller/Download/SampleTest.php | 2 +- .../Catalog/Product/ConfigurationTest.php | 2 +- .../Test/Unit/Helper/DownloadTest.php | 2 +- .../Unit/Model/File/ContentValidatorTest.php | 2 +- .../Test/Unit/Model/Link/BuilderTest.php | 2 +- .../Unit/Model/Link/ContentValidatorTest.php | 2 +- .../Unit/Model/Link/CreateHandlerTest.php | 2 +- .../Unit/Model/Link/UpdateHandlerTest.php | 2 +- .../Test/Unit/Model/LinkRepositoryTest.php | 2 +- .../CopyConstructor/DownloadableTest.php | 2 +- .../CopyConstructor/_files/expected_data.php | 2 +- .../Model/Product/TypeHandler/LinkTest.php | 2 +- .../Model/Product/TypeHandler/SampleTest.php | 2 +- .../Test/Unit/Model/Product/TypeTest.php | 129 +- .../Plugin/DownloadableTest.php | 2 +- .../Unit/Model/ProductOptionProcessorTest.php | 2 +- .../Quote/Item/CartItemProcessorTest.php | 2 +- .../Sales/Order/Pdf/Items/CreditmemoTest.php | 2 +- .../Test/Unit/Model/Sample/BuilderTest.php | 2 +- .../Model/Sample/ContentValidatorTest.php | 2 +- .../Unit/Model/Sample/CreateHandlerTest.php | 2 +- .../Unit/Model/Sample/UpdateHandlerTest.php | 2 +- .../Test/Unit/Model/SampleRepositoryTest.php | 2 +- .../IsAllowedGuestCheckoutObserverTest.php | 2 +- .../SaveDownloadableOrderItemObserverTest.php | 2 +- .../Observer/SetLinkStatusObserverTest.php | 2 +- .../Test/Unit/Pricing/Price/LinkPriceTest.php | 2 +- .../Product/Form/Modifier/CompositeTest.php | 2 +- .../Product/Form/Modifier/Data/LinksTest.php | 2 +- .../Form/Modifier/DownloadablePanelTest.php | 2 +- .../Product/Form/Modifier/LinksTest.php | 2 +- .../Product/Form/Modifier/SamplesTest.php | 2 +- .../Test/Unit/_files/download_mock.php | 2 +- .../Product/Form/Modifier/Composite.php | 2 +- .../Product/Form/Modifier/Data/Links.php | 2 +- .../Product/Form/Modifier/Data/Samples.php | 2 +- .../Form/Modifier/DownloadablePanel.php | 2 +- .../Product/Form/Modifier/Links.php | 2 +- .../Product/Form/Modifier/Samples.php | 2 +- .../Product/Form/Modifier/UsedDefault.php | 2 +- app/code/Magento/Downloadable/composer.json | 2 +- app/code/Magento/Downloadable/etc/acl.xml | 2 +- .../Magento/Downloadable/etc/adminhtml/di.xml | 2 +- .../Downloadable/etc/adminhtml/menu.xml | 2 +- .../Downloadable/etc/adminhtml/routes.xml | 2 +- .../Downloadable/etc/adminhtml/system.xml | 2 +- .../Downloadable/etc/catalog_attributes.xml | 2 +- app/code/Magento/Downloadable/etc/config.xml | 2 +- app/code/Magento/Downloadable/etc/di.xml | 30 +- app/code/Magento/Downloadable/etc/events.xml | 2 +- .../Downloadable/etc/extension_attributes.xml | 2 +- .../Magento/Downloadable/etc/fieldset.xml | 2 +- .../Magento/Downloadable/etc/frontend/di.xml | 2 +- .../Downloadable/etc/frontend/events.xml | 2 +- .../Downloadable/etc/frontend/page_types.xml | 2 +- .../Downloadable/etc/frontend/routes.xml | 4 +- app/code/Magento/Downloadable/etc/module.xml | 4 +- app/code/Magento/Downloadable/etc/pdf.xml | 2 +- .../Downloadable/etc/product_types.xml | 2 +- app/code/Magento/Downloadable/etc/sales.xml | 2 +- app/code/Magento/Downloadable/etc/webapi.xml | 2 +- .../Downloadable/etc/webapi_rest/di.xml | 2 +- .../Downloadable/etc/webapi_soap/di.xml | 2 +- .../Magento/Downloadable/registration.php | 2 +- .../layout/catalog_product_downloadable.xml | 2 +- .../layout/catalog_product_simple.xml | 2 +- ...catalog_product_view_type_downloadable.xml | 2 +- .../layout/catalog_product_virtual.xml | 2 +- .../layout/customer_index_wishlist.xml | 2 +- .../adminhtml/layout/downloadable_items.xml | 2 +- .../layout/sales_order_creditmemo_new.xml | 2 +- .../sales_order_creditmemo_updateqty.xml | 2 +- .../layout/sales_order_creditmemo_view.xml | 2 +- .../layout/sales_order_invoice_new.xml | 2 +- .../layout/sales_order_invoice_updateqty.xml | 2 +- .../layout/sales_order_invoice_view.xml | 2 +- .../adminhtml/layout/sales_order_view.xml | 2 +- .../composite/fieldset/downloadable.phtml | 2 +- .../templates/product/edit/downloadable.phtml | 2 +- .../product/edit/downloadable/links.phtml | 5 +- .../product/edit/downloadable/samples.phtml | 5 +- .../column/downloadable/creditmemo/name.phtml | 2 +- .../column/downloadable/invoice/name.phtml | 2 +- .../items/column/downloadable/name.phtml | 2 +- .../web/downloadable-type-handler.js | 2 +- .../web/js/components/file-uploader.js | 2 +- .../js/components/is-downloadable-handler.js | 2 +- .../web/js/components/price-handler.js | 2 +- .../web/js/components/upload-type-handler.js | 2 +- .../components/use-price-default-handler.js | 2 +- .../template/components/file-uploader.html | 9 +- ...catalog_product_view_type_downloadable.xml | 2 +- ...ckout_cart_configure_type_downloadable.xml | 2 +- .../layout/checkout_cart_item_renderers.xml | 2 +- ...checkout_onepage_review_item_renderers.xml | 2 +- .../layout/checkout_onepage_success.xml | 2 +- .../view/frontend/layout/customer_account.xml | 5 +- .../layout/downloadable_customer_products.xml | 2 +- .../layout/multishipping_checkout_success.xml | 2 +- ...sales_email_order_creditmemo_renderers.xml | 2 +- .../sales_email_order_invoice_renderers.xml | 2 +- .../layout/sales_email_order_renderers.xml | 2 +- .../sales_order_creditmemo_renderers.xml | 2 +- .../layout/sales_order_invoice_renderers.xml | 2 +- .../layout/sales_order_item_renderers.xml | 2 +- ...sales_order_print_creditmemo_renderers.xml | 2 +- .../sales_order_print_invoice_renderers.xml | 2 +- .../layout/sales_order_print_renderers.xml | 2 +- .../view/frontend/requirejs-config.js | 4 +- .../templates/catalog/product/links.phtml | 2 +- .../templates/catalog/product/samples.phtml | 2 +- .../templates/catalog/product/type.phtml | 2 +- .../frontend/templates/checkout/success.phtml | 2 +- .../templates/customer/products/list.phtml | 2 +- .../order/items/creditmemo/downloadable.phtml | 2 +- .../order/items/invoice/downloadable.phtml | 2 +- .../order/items/order/downloadable.phtml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../items/renderer/downloadable.phtml | 2 +- .../invoice/items/renderer/downloadable.phtml | 2 +- .../order/items/renderer/downloadable.phtml | 2 +- .../view/frontend/web/downloadable.js | 48 +- .../DownloadableImportExport/Helper/Data.php | 2 +- .../Helper/Uploader.php | 2 +- .../Import/Product/Type/Downloadable.php | 14 +- .../Import/Product/Type/DownloadableTest.php | 25 +- .../DownloadableImportExport/composer.json | 2 +- .../DownloadableImportExport/etc/import.xml | 2 +- .../DownloadableImportExport/etc/module.xml | 2 +- .../DownloadableImportExport/registration.php | 2 +- .../Api/AttributeGroupRepositoryInterface.php | 2 +- .../Eav/Api/AttributeManagementInterface.php | 2 +- .../AttributeOptionManagementInterface.php | 2 +- .../Eav/Api/AttributeRepositoryInterface.php | 2 +- .../Api/AttributeSetManagementInterface.php | 2 +- .../Api/AttributeSetRepositoryInterface.php | 2 +- .../Data/AttributeDefaultValueInterface.php | 2 +- .../Data/AttributeFrontendLabelInterface.php | 2 +- .../Eav/Api/Data/AttributeGroupInterface.php | 2 +- .../AttributeGroupSearchResultsInterface.php | 2 +- .../Eav/Api/Data/AttributeInterface.php | 2 +- .../Eav/Api/Data/AttributeOptionInterface.php | 2 +- .../Data/AttributeOptionLabelInterface.php | 2 +- .../Data/AttributeSearchResultsInterface.php | 2 +- .../Eav/Api/Data/AttributeSetInterface.php | 2 +- .../AttributeSetSearchResultsInterface.php | 2 +- .../Data/AttributeValidationRuleInterface.php | 2 +- .../Eav/Block/Adminhtml/Attribute/Edit/Js.php | 2 +- .../Attribute/Edit/Main/AbstractMain.php | 2 +- .../Edit/Options/AbstractOptions.php | 2 +- .../Attribute/Edit/Options/Labels.php | 2 +- .../Attribute/Edit/Options/Options.php | 2 +- .../Adminhtml/Attribute/Grid/AbstractGrid.php | 2 +- .../Adminhtml/Attribute/PropertyLocker.php | 2 +- app/code/Magento/Eav/Helper/Data.php | 2 +- .../Attribute/Validation/Rules/Options.php | 2 +- .../System/Config/Source/Inputtype.php | 2 +- .../Config/Source/Inputtype/Validator.php | 2 +- .../CollectionProcessor/FilterProcessor.php | 2 +- .../AttributeGroupAttributeSetIdFilter.php | 2 +- .../AttributeGroupCodeFilter.php | 2 +- .../AttributeSetEntityTypeCodeFilter.php | 2 +- app/code/Magento/Eav/Model/Attribute.php | 6 +- .../Eav/Model/Attribute/Data/AbstractData.php | 4 +- .../Eav/Model/Attribute/Data/Boolean.php | 2 +- .../Magento/Eav/Model/Attribute/Data/Date.php | 2 +- .../Magento/Eav/Model/Attribute/Data/File.php | 2 +- .../Eav/Model/Attribute/Data/Hidden.php | 2 +- .../Eav/Model/Attribute/Data/Image.php | 2 +- .../Eav/Model/Attribute/Data/Multiline.php | 2 +- .../Eav/Model/Attribute/Data/Multiselect.php | 2 +- .../Eav/Model/Attribute/Data/Select.php | 2 +- .../Magento/Eav/Model/Attribute/Data/Text.php | 2 +- .../Eav/Model/Attribute/Data/Textarea.php | 2 +- .../Eav/Model/Attribute/GroupRepository.php | 2 +- .../Eav/Model/AttributeDataFactory.php | 2 +- .../Magento/Eav/Model/AttributeFactory.php | 2 +- .../Magento/Eav/Model/AttributeManagement.php | 35 +- .../Magento/Eav/Model/AttributeProvider.php | 2 +- .../Magento/Eav/Model/AttributeRepository.php | 2 +- .../Eav/Model/AttributeSetManagement.php | 2 +- .../Eav/Model/AttributeSetRepository.php | 2 +- app/code/Magento/Eav/Model/Cache/Type.php | 2 +- app/code/Magento/Eav/Model/Config.php | 52 +- .../Eav/Model}/CustomAttributesMapper.php | 7 +- .../Model/EavCustomAttributeTypeLocator.php | 2 +- .../ComplexType.php | 2 +- .../SimpleType.php | 2 +- app/code/Magento/Eav/Model/Entity.php | 2 +- .../Eav/Model/Entity/AbstractEntity.php | 18 +- .../Magento/Eav/Model/Entity/Attribute.php | 2 +- .../Entity/Attribute/AbstractAttribute.php | 188 +- .../AttributeGroupAlreadyExistsException.php | 15 + .../Entity/Attribute/AttributeInterface.php | 2 +- .../Attribute/Backend/AbstractBackend.php | 2 +- .../Entity/Attribute/Backend/ArrayBackend.php | 4 +- .../Attribute/Backend/BackendInterface.php | 2 +- .../Entity/Attribute/Backend/Datetime.php | 2 +- .../Attribute/Backend/DefaultBackend.php | 2 +- .../Entity/Attribute/Backend/Increment.php | 2 +- .../Entity/Attribute/Backend/Serialized.php | 2 +- .../Model/Entity/Attribute/Backend/Store.php | 2 +- .../Entity/Attribute/Backend/Time/Created.php | 2 +- .../Entity/Attribute/Backend/Time/Updated.php | 2 +- .../Eav/Model/Entity/Attribute/Config.php | 20 +- .../Entity/Attribute/Config/Converter.php | 2 +- .../Model/Entity/Attribute/Config/Reader.php | 2 +- .../Entity/Attribute/Config/SchemaLocator.php | 2 +- .../Eav/Model/Entity/Attribute/Exception.php | 2 +- .../Attribute/Frontend/AbstractFrontend.php | 2 +- .../Entity/Attribute/Frontend/Datetime.php | 2 +- .../Attribute/Frontend/DefaultFrontend.php | 2 +- .../Attribute/Frontend/FrontendInterface.php | 2 +- .../Model/Entity/Attribute/FrontendLabel.php | 2 +- .../Eav/Model/Entity/Attribute/Group.php | 2 +- .../Eav/Model/Entity/Attribute/Option.php | 2 +- .../Model/Entity/Attribute/OptionLabel.php | 2 +- .../Entity/Attribute/OptionManagement.php | 2 +- .../Attribute/ScopedAttributeInterface.php | 2 +- .../Eav/Model/Entity/Attribute/Set.php | 2 +- .../Attribute/Source/AbstractSource.php | 2 +- .../Model/Entity/Attribute/Source/Boolean.php | 2 +- .../Model/Entity/Attribute/Source/Config.php | 2 +- .../Attribute/Source/SourceInterface.php | 2 +- .../Model/Entity/Attribute/Source/Store.php | 2 +- .../Model/Entity/Attribute/Source/Table.php | 42 +- .../Model/Entity/Attribute/ValidationRule.php | 2 +- .../Eav/Model/Entity/AttributeCache.php | 5 +- .../Eav/Model/Entity/AttributeLoader.php | 2 +- .../Model/Entity/AttributeLoaderInterface.php | 2 +- .../Entity/Collection/AbstractCollection.php | 2 +- .../VersionControl/AbstractCollection.php | 2 +- app/code/Magento/Eav/Model/Entity/Context.php | 2 +- .../Eav/Model/Entity/EntityInterface.php | 2 +- .../Entity/Increment/AbstractIncrement.php | 2 +- .../Eav/Model/Entity/Increment/Alphanum.php | 2 +- .../Entity/Increment/IncrementInterface.php | 2 +- .../Model/Entity/Increment/NumericValue.php | 2 +- .../Eav/Model/Entity/Setup/Context.php | 2 +- .../Eav/Model/Entity/Setup/PropertyMapper.php | 2 +- .../Entity/Setup/PropertyMapper/Composite.php | 2 +- .../Entity/Setup/PropertyMapperAbstract.php | 2 +- .../Entity/Setup/PropertyMapperInterface.php | 2 +- app/code/Magento/Eav/Model/Entity/Store.php | 2 +- app/code/Magento/Eav/Model/Entity/Type.php | 2 +- .../Entity/VersionControl/AbstractEntity.php | 2 +- .../Model/Entity/VersionControl/Metadata.php | 2 +- app/code/Magento/Eav/Model/Form.php | 2 +- app/code/Magento/Eav/Model/Form/Element.php | 2 +- app/code/Magento/Eav/Model/Form/Factory.php | 2 +- app/code/Magento/Eav/Model/Form/Fieldset.php | 2 +- app/code/Magento/Eav/Model/Form/Type.php | 2 +- .../Eav/Model/ResourceModel/Attribute.php | 4 +- .../ResourceModel/Attribute/Collection.php | 2 +- .../ProviderInterface.php | 2 +- .../Model/ResourceModel/AttributeLoader.php | 97 + .../ResourceModel/AttributePersistor.php | 11 +- .../Eav/Model/ResourceModel/Config.php | 2 +- .../Eav/Model/ResourceModel/CreateHandler.php | 49 +- .../Model/ResourceModel/Entity/Attribute.php | 2 +- .../Entity/Attribute/Collection.php | 2 +- .../Entity/Attribute/Grid/Collection.php | 2 +- .../ResourceModel/Entity/Attribute/Group.php | 76 +- .../Entity/Attribute/Group/Collection.php | 2 +- .../ResourceModel/Entity/Attribute/Option.php | 2 +- .../Entity/Attribute/Option/Collection.php | 2 +- .../ResourceModel/Entity/Attribute/Set.php | 18 +- .../Entity/Attribute/Set/Collection.php | 2 +- .../Eav/Model/ResourceModel/Entity/Store.php | 2 +- .../Eav/Model/ResourceModel/Entity/Type.php | 14 +- .../ResourceModel/Entity/Type/Collection.php | 2 +- .../Model/ResourceModel/Form/Attribute.php | 2 +- .../Form/Attribute/Collection.php | 2 +- .../Eav/Model/ResourceModel/Form/Element.php | 2 +- .../ResourceModel/Form/Element/Collection.php | 2 +- .../Eav/Model/ResourceModel/Form/Fieldset.php | 2 +- .../Form/Fieldset/Collection.php | 2 +- .../Eav/Model/ResourceModel/Form/Type.php | 2 +- .../ResourceModel/Form/Type/Collection.php | 2 +- .../Eav/Model/ResourceModel/Helper.php | 2 +- .../Eav/Model/ResourceModel/ReadHandler.php | 2 +- .../Eav/Model/ResourceModel/ReadSnapshot.php | 2 +- .../Eav/Model/ResourceModel/UpdateHandler.php | 126 +- .../Eav/Model/Validator/Attribute/Backend.php | 2 +- .../Eav/Model/Validator/Attribute/Data.php | 2 +- .../Model/ResourceModel/Entity/Attribute.php | 65 +- app/code/Magento/Eav/Setup/EavSetup.php | 2 +- app/code/Magento/Eav/Setup/InstallData.php | 2 +- app/code/Magento/Eav/Setup/InstallSchema.php | 2 +- app/code/Magento/Eav/Setup/UpgradeSchema.php | 48 + .../Attribute/PropertyLockerTest.php | 2 +- .../Magento/Eav/Test/Unit/Helper/DataTest.php | 2 +- .../Validation/Rules/OptionsTest.php | 2 +- .../Config/Source/Inputtype/ValidatorTest.php | 2 +- .../System/Config/Source/InputtypeTest.php | 2 +- ...AttributeGroupAttributeSetIdFilterTest.php | 2 +- .../AttributeGroupCodeFilterTest.php | 2 +- .../AttributeSetEntityTypeCodeFilterTest.php | 2 +- .../FilterProcessorTest.php | 2 +- .../Model/Attribute/Data/AbstractDataTest.php | 40 +- .../Unit/Model/Attribute/Data/BooleanTest.php | 2 +- .../Unit/Model/Attribute/Data/DateTest.php | 2 +- .../Unit/Model/Attribute/Data/FileTest.php | 2 +- .../Unit/Model/Attribute/Data/ImageTest.php | 2 +- .../Model/Attribute/Data/MultilineTest.php | 2 +- .../Model/Attribute/Data/MultiselectTest.php | 2 +- .../Unit/Model/Attribute/Data/SelectTest.php | 2 +- .../Unit/Model/Attribute/Data/TextTest.php | 2 +- .../Model/Attribute/GroupRepositoryTest.php | 2 +- .../Test/Unit/Model/AttributeFactoryTest.php | 2 +- .../Unit/Model/AttributeManagementTest.php | 20 +- .../Unit/Model/AttributeRepositoryTest.php | 2 +- .../Unit/Model/AttributeSetManagementTest.php | 2 +- .../Unit/Model/AttributeSetRepositoryTest.php | 2 +- .../Eav/Test/Unit/Model/ConfigTest.php | 206 +- .../Model}/CustomAttributesMapperTest.php | 37 +- .../EavCustomAttributeTypeLocatorTest.php | 2 +- .../Unit/Model/Entity/AbstractEntityTest.php | 46 +- .../Attribute/AbstractAttributeTest.php | 40 +- .../Entity/Attribute/Backend/AbstractTest.php | 2 +- .../{ArrayTest.php => ArrayBackendTest.php} | 7 +- .../Entity/Attribute/Config/ConverterTest.php | 2 +- .../Model/Entity/Attribute/Config/XsdTest.php | 2 +- .../Config/_files/eav_attributes.php | 2 +- .../Config/_files/eav_attributes.xml | 2 +- .../_files/invalidEavAttributeXmlArray.php | 2 +- .../Model/Entity/Attribute/ConfigTest.php | 24 +- .../Attribute/Frontend/DatetimeTest.php | 2 +- .../Frontend/DefaultFrontendTest.php | 2 +- .../Unit/Model/Entity/Attribute/GroupTest.php | 2 +- .../Entity/Attribute/OptionManagementTest.php | 2 +- .../Unit/Model/Entity/Attribute/SetTest.php | 2 +- .../Entity/Attribute/Source/BooleanTest.php | 2 +- .../Entity/Attribute/Source/TableTest.php | 216 +- .../Test/Unit/Model/Entity/AttributeTest.php | 2 +- .../Collection/AbstractCollectionStub.php | 2 +- .../Collection/AbstractCollectionTest.php | 2 +- .../VersionControl/AbstractCollectionStub.php | 2 +- .../VersionControl/AbstractCollectionTest.php | 2 +- .../Model/Entity/Increment/AlphanumTest.php | 2 +- .../Model/Entity/Increment/NumericTest.php | 2 +- .../Eav/Test/Unit/Model/Entity/TypeTest.php | 2 +- .../VersionControl/AbstractEntityTest.php | 2 +- .../Entity/VersionControl/MetadataTest.php | 2 +- .../Magento/Eav/Test/Unit/Model/FormTest.php | 2 +- .../Attribute/CollectionTest.php | 2 +- .../Attribute/Option/CollectionTest.php | 2 +- .../Entity/Attribute/SetTest.php | 143 +- .../ResourceModel/Entity/AttributeTest.php | 4 +- .../Model/Validator/Attribute/BackendTest.php | 2 +- .../Model/Validator/Attribute/DataTest.php | 2 +- .../ResourceModel/Entity/AttributeTest.php | 199 +- .../_files/describe_table_eav_attribute.php | 2 +- app/code/Magento/Eav/composer.json | 2 +- app/code/Magento/Eav/etc/cache.xml | 2 +- app/code/Magento/Eav/etc/config.xml | 2 +- app/code/Magento/Eav/etc/di.xml | 35 +- app/code/Magento/Eav/etc/eav_attributes.xsd | 2 +- .../Magento/Eav/etc/extension_attributes.xml | 2 +- app/code/Magento/Eav/etc/module.xml | 4 +- app/code/Magento/Eav/etc/validation.xml | 2 +- app/code/Magento/Eav/etc/webapi.xml | 2 +- app/code/Magento/Eav/registration.php | 2 +- .../templates/attribute/edit/js.phtml | 2 +- .../Email/Block/Adminhtml/Template.php | 2 +- .../Email/Block/Adminhtml/Template/Edit.php | 2 +- .../Block/Adminhtml/Template/Edit/Form.php | 2 +- .../Adminhtml/Template/Grid/Filter/Type.php | 2 +- .../Template/Grid/Renderer/Action.php | 2 +- .../Template/Grid/Renderer/Sender.php | 2 +- .../Adminhtml/Template/Grid/Renderer/Type.php | 2 +- .../Block/Adminhtml/Template/Preview.php | 2 +- .../Controller/Adminhtml/Email/Template.php | 2 +- .../Email/Template/DefaultTemplate.php | 2 +- .../Adminhtml/Email/Template/Delete.php | 2 +- .../Adminhtml/Email/Template/Edit.php | 2 +- .../Adminhtml/Email/Template/Grid.php | 2 +- .../Adminhtml/Email/Template/Index.php | 2 +- .../Adminhtml/Email/Template/NewAction.php | 2 +- .../Adminhtml/Email/Template/Preview.php | 2 +- .../Adminhtml/Email/Template/Save.php | 2 +- .../Magento/Email/Model/AbstractTemplate.php | 8 +- .../Magento/Email/Model/BackendTemplate.php | 2 +- .../Email/Model/Design/Backend/Logo.php | 2 +- .../Model/Mail/TransportInterfacePlugin.php | 51 + .../Email/Model/Plugin/WindowsSmtpConfig.php | 2 +- .../Email/Model/ResourceModel/Template.php | 2 +- .../ResourceModel/Template/Collection.php | 2 +- .../Magento/Email/Model/Source/Variables.php | 2 +- app/code/Magento/Email/Model/Template.php | 25 +- .../Magento/Email/Model/Template/Config.php | 2 +- .../Email/Model/Template/Config/Converter.php | 2 +- .../Email/Model/Template/Config/Data.php | 19 +- .../Model/Template/Config/FileIterator.php | 2 +- .../Model/Template/Config/FileResolver.php | 2 +- .../Email/Model/Template/Config/Reader.php | 2 +- .../Model/Template/Config/SchemaLocator.php | 2 +- .../Email/Model/Template/Css/Processor.php | 5 +- .../Magento/Email/Model/Template/Filter.php | 52 +- .../Email/Model/Template/SenderResolver.php | 2 +- .../Magento/Email/Setup/InstallSchema.php | 2 +- .../Adminhtml/Template/Edit/FormTest.php | 2 +- .../Block/Adminhtml/Template/EditTest.php | 2 +- .../Template/Grid/Renderer/ActionTest.php | 2 +- .../Template/Grid/Renderer/SenderTest.php | 2 +- .../Template/Grid/Renderer/TypeTest.php | 2 +- .../Block/Adminhtml/Template/PreviewTest.php | 2 +- .../Unit/Block/Adminhtml/TemplateTest.php | 2 +- .../Adminhtml/Email/Template/EditTest.php | 2 +- .../Adminhtml/Email/Template/IndexTest.php | 2 +- .../Adminhtml/Email/Template/PreviewTest.php | 2 +- .../Test/Unit/Model/AbstractTemplateTest.php | 87 +- .../Test/Unit/Model/BackendTemplateTest.php | 2 +- .../Test/Unit/Model/Source/VariablesTest.php | 2 +- .../Model/Template/Config/ConverterTest.php | 2 +- .../Template/Config/FileIteratorTest.php | 2 +- .../Template/Config/FileResolverTest.php | 2 +- .../Unit/Model/Template/Config/ReaderTest.php | 2 +- .../Template/Config/SchemaLocatorTest.php | 2 +- .../Unit/Model/Template/Config/XsdTest.php | 2 +- .../ModuleOne/etc/email_templates_one.xml | 2 +- .../ModuleTwo/etc/email_templates_two.xml | 2 +- .../Config/_files/email_templates_merged.php | 2 +- .../Config/_files/email_templates_merged.xml | 2 +- .../Test/Unit/Model/Template/ConfigTest.php | 2 +- .../Unit/Model/Template/Css/ProcessorTest.php | 2 +- .../Test/Unit/Model/Template/FilterTest.php | 2 +- .../Email/Test/Unit/Model/TemplateTest.php | 25 +- app/code/Magento/Email/composer.json | 2 +- app/code/Magento/Email/etc/acl.xml | 2 +- app/code/Magento/Email/etc/adminhtml/di.xml | 2 +- app/code/Magento/Email/etc/adminhtml/menu.xml | 2 +- .../Magento/Email/etc/adminhtml/routes.xml | 2 +- app/code/Magento/Email/etc/config.xml | 2 +- app/code/Magento/Email/etc/di.xml | 3 +- .../Magento/Email/etc/email_templates.xml | 2 +- .../Magento/Email/etc/email_templates.xsd | 2 +- app/code/Magento/Email/etc/frontend/di.xml | 2 +- app/code/Magento/Email/etc/module.xml | 2 +- app/code/Magento/Email/registration.php | 2 +- .../layout/adminhtml_email_template_grid.xml | 2 +- .../adminhtml_email_template_grid_block.xml | 2 +- .../layout/adminhtml_email_template_index.xml | 2 +- .../adminhtml_email_template_preview.xml | 5 +- .../adminhtml/templates/template/edit.phtml | 2 +- .../adminhtml/templates/template/list.phtml | 2 +- .../templates/template/preview.phtml | 2 +- .../ui_component/design_config_form.xml | 2 +- .../Email/view/frontend/email/footer.html | 2 +- .../Email/view/frontend/email/header.html | 2 +- .../Block/Adminhtml/Crypt/Key/Edit.php | 2 +- .../Block/Adminhtml/Crypt/Key/Form.php | 2 +- .../Controller/Adminhtml/Crypt/Key.php | 2 +- .../Controller/Adminhtml/Crypt/Key/Index.php | 2 +- .../Controller/Adminhtml/Crypt/Key/Save.php | 2 +- .../Model/ResourceModel/Key/Change.php | 2 +- .../Adminhtml/Crypt/Key/SaveTest.php | 2 +- .../Model/ResourceModel/Key/ChangeTest.php | 2 +- app/code/Magento/EncryptionKey/composer.json | 2 +- app/code/Magento/EncryptionKey/etc/acl.xml | 2 +- .../EncryptionKey/etc/adminhtml/menu.xml | 2 +- .../EncryptionKey/etc/adminhtml/routes.xml | 2 +- app/code/Magento/EncryptionKey/etc/config.xml | 2 +- app/code/Magento/EncryptionKey/etc/module.xml | 2 +- .../Magento/EncryptionKey/registration.php | 2 +- .../layout/adminhtml_crypt_key_index.xml | 2 +- app/code/Magento/Fedex/Model/Carrier.php | 2 +- .../Magento/Fedex/Model/Source/Dropoff.php | 2 +- .../Magento/Fedex/Model/Source/Freemethod.php | 2 +- .../Magento/Fedex/Model/Source/Generic.php | 2 +- .../Magento/Fedex/Model/Source/Method.php | 2 +- .../Magento/Fedex/Model/Source/Packaging.php | 2 +- .../Fedex/Model/Source/Unitofmeasure.php | 2 +- app/code/Magento/Fedex/Setup/InstallData.php | 2 +- .../Fedex/Test/Unit/Model/CarrierTest.php | 2 +- app/code/Magento/Fedex/composer.json | 2 +- .../Magento/Fedex/etc/adminhtml/system.xml | 2 +- app/code/Magento/Fedex/etc/config.xml | 2 +- app/code/Magento/Fedex/etc/di.xml | 19 + app/code/Magento/Fedex/etc/module.xml | 2 +- app/code/Magento/Fedex/registration.php | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../model/shipping-rates-validation-rules.js | 46 +- .../web/js/model/shipping-rates-validator.js | 60 +- .../web/js/view/shipping-rates-validation.js | 46 +- .../Api/CartRepositoryInterface.php | 2 +- .../GiftMessage/Api/Data/MessageInterface.php | 2 +- .../Api/GuestCartRepositoryInterface.php | 2 +- .../Api/GuestItemRepositoryInterface.php | 2 +- .../Api/ItemRepositoryInterface.php | 2 +- .../Api/OrderItemRepositoryInterface.php | 2 +- .../Api/OrderRepositoryInterface.php | 2 +- .../Adminhtml/Product/Helper/Form/Config.php | 2 +- .../Adminhtml/Sales/Order/Create/Form.php | 2 +- .../Sales/Order/Create/Giftoptions.php | 2 +- .../Adminhtml/Sales/Order/Create/Items.php | 2 +- .../Block/Adminhtml/Sales/Order/View/Form.php | 2 +- .../Sales/Order/View/Giftoptions.php | 2 +- .../Adminhtml/Sales/Order/View/Items.php | 2 +- .../GiftMessage/Block/Cart/GiftOptions.php | 2 +- .../Item/Renderer/Actions/GiftOptions.php | 2 +- .../Item/Renderer/Actions/ItemIdProcessor.php | 2 +- .../Actions/LayoutProcessorInterface.php | 2 +- .../GiftMessage/Block/Message/Inline.php | 2 +- .../Message/Multishipping/Plugin/ItemsBox.php | 2 +- .../Magento/GiftMessage/Helper/Message.php | 2 +- .../GiftMessage/Model/CartRepository.php | 2 +- .../Model/CompositeConfigProvider.php | 2 +- .../Model/GiftMessageConfigProvider.php | 2 +- .../GiftMessage/Model/GiftMessageManager.php | 2 +- .../GiftMessage/Model/GuestCartRepository.php | 2 +- .../GiftMessage/Model/GuestItemRepository.php | 2 +- .../GiftMessage/Model/ItemRepository.php | 2 +- .../Magento/GiftMessage/Model/Message.php | 2 +- .../GiftMessage/Model/OrderItemRepository.php | 25 +- .../GiftMessage/Model/OrderRepository.php | 2 +- .../GiftMessage/Model/Plugin/OrderGet.php | 2 +- .../GiftMessage/Model/Plugin/OrderSave.php | 2 +- .../GiftMessage/Model/Plugin/QuoteItem.php | 2 +- .../Model/ResourceModel/Message.php | 2 +- .../ResourceModel/Message/Collection.php | 2 +- app/code/Magento/GiftMessage/Model/Save.php | 2 +- .../Model/Type/Plugin/Multishipping.php | 2 +- .../GiftMessage/Model/Type/Plugin/Onepage.php | 2 +- .../Magento/GiftMessage/Model/TypeFactory.php | 2 +- ...MultishippingEventCreateOrdersObserver.php | 2 +- ...SalesEventOrderItemToQuoteItemObserver.php | 2 +- .../SalesEventOrderToQuoteObserver.php | 2 +- .../SalesEventQuoteSubmitBeforeObserver.php | 2 +- .../Magento/GiftMessage/Setup/InstallData.php | 2 +- .../GiftMessage/Setup/InstallSchema.php | 2 +- .../Magento/GiftMessage/Setup/UpgradeData.php | 2 +- .../Test/Unit/Block/Cart/GiftOptionsTest.php | 2 +- .../Item/Renderer/Actions/GiftOptionsTest.php | 2 +- .../Renderer/Actions/ItemIdProcessorTest.php | 2 +- .../Test/Unit/Block/Message/InlineTest.php | 2 +- .../Test/Unit/Helper/MessageTest.php | 2 +- .../Test/Unit/Model/CartRepositoryTest.php | 2 +- .../Model/CompositeConfigProviderTest.php | 2 +- .../Model/GiftMessageConfigProviderTest.php | 2 +- .../Unit/Model/GiftMessageManagerTest.php | 2 +- .../Unit/Model/GuestCartRepositoryTest.php | 2 +- .../Unit/Model/GuestItemRepositoryTest.php | 2 +- .../Test/Unit/Model/ItemRepositoryTest.php | 2 +- .../Unit/Model/OrderItemRepositoryTest.php | 490 ++ .../Test/Unit/Model/Plugin/OrderGetTest.php | 2 +- .../Test/Unit/Model/Plugin/OrderSaveTest.php | 2 +- .../Test/Unit/Model/Plugin/QuoteItemTest.php | 2 +- .../GiftMessage/Test/Unit/Model/SaveTest.php | 2 +- .../Model/Type/Plugin/MultishippingTest.php | 2 +- .../Unit/Model/Type/Plugin/OnepageTest.php | 2 +- ...ishippingEventCreateOrdersObserverTest.php | 2 +- ...alesEventQuoteSubmitBeforeObserverTest.php | 2 +- .../Product/Modifier/GiftMessageTest.php | 2 +- .../Product/Modifier/GiftMessage.php | 2 +- app/code/Magento/GiftMessage/composer.json | 2 +- .../Magento/GiftMessage/etc/adminhtml/di.xml | 2 +- .../GiftMessage/etc/adminhtml/events.xml | 2 +- .../GiftMessage/etc/adminhtml/system.xml | 2 +- .../GiftMessage/etc/catalog_attributes.xml | 2 +- app/code/Magento/GiftMessage/etc/config.xml | 2 +- app/code/Magento/GiftMessage/etc/di.xml | 2 +- .../GiftMessage/etc/extension_attributes.xml | 2 +- app/code/Magento/GiftMessage/etc/fieldset.xml | 4 +- .../Magento/GiftMessage/etc/frontend/di.xml | 2 +- .../GiftMessage/etc/frontend/events.xml | 2 +- .../GiftMessage/etc/frontend/routes.xml | 4 +- app/code/Magento/GiftMessage/etc/module.xml | 2 +- app/code/Magento/GiftMessage/etc/webapi.xml | 2 +- .../GiftMessage/etc/webapi_rest/events.xml | 2 +- app/code/Magento/GiftMessage/registration.php | 2 +- .../layout/sales_order_create_index.xml | 2 +- .../sales_order_create_load_block_data.xml | 2 +- .../sales_order_create_load_block_items.xml | 2 +- .../adminhtml/layout/sales_order_view.xml | 2 +- .../adminhtml/templates/giftoptionsform.phtml | 2 +- .../view/adminhtml/templates/popup.phtml | 2 +- .../sales/order/create/giftoptions.phtml | 2 +- .../templates/sales/order/create/items.phtml | 2 +- .../sales/order/view/giftoptions.phtml | 2 +- .../templates/sales/order/view/items.phtml | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../layout/checkout_cart_item_renderers.xml | 2 +- .../view/frontend/requirejs-config.js | 4 +- .../templates/cart/gift_options.phtml | 2 +- .../item/renderer/actions/gift_options.phtml | 2 +- .../view/frontend/templates/inline.phtml | 2 +- .../view/frontend/web/extra-options.js | 24 +- .../view/frontend/web/gift-options.js | 41 +- .../frontend/web/js/action/gift-options.js | 106 +- .../frontend/web/js/model/gift-message.js | 313 +- .../frontend/web/js/model/gift-options.js | 64 +- .../view/frontend/web/js/model/url-builder.js | 28 +- .../view/frontend/web/js/view/gift-message.js | 260 +- .../web/template/gift-message-form.html | 2 +- .../web/template/gift-message-item-level.html | 2 +- .../frontend/web/template/gift-message.html | 2 +- app/code/Magento/GoogleAdwords/Block/Code.php | 2 +- .../Magento/GoogleAdwords/Helper/Data.php | 2 +- .../Config/Backend/AbstractConversion.php | 2 +- .../Model/Config/Backend/Color.php | 2 +- .../Model/Config/Backend/ConversionId.php | 2 +- .../Model/Config/Source/Language.php | 2 +- .../Model/Config/Source/ValueType.php | 2 +- .../Model/Filter/UppercaseTitle.php | 2 +- .../GoogleAdwords/Model/Validator/Factory.php | 2 +- .../Observer/SetConversionValueObserver.php | 2 +- .../Test/Unit/Helper/DataTest.php | 2 +- .../Model/Config/Source/ValueTypeTest.php | 2 +- .../Unit/Model/Filter/UppercaseTitleTest.php | 2 +- .../Test/Unit/Model/Validator/FactoryTest.php | 2 +- .../SetConversionValueObserverTest.php | 2 +- app/code/Magento/GoogleAdwords/composer.json | 2 +- .../GoogleAdwords/etc/adminhtml/system.xml | 2 +- app/code/Magento/GoogleAdwords/etc/config.xml | 4 +- app/code/Magento/GoogleAdwords/etc/di.xml | 2 +- .../GoogleAdwords/etc/frontend/events.xml | 2 +- app/code/Magento/GoogleAdwords/etc/module.xml | 2 +- .../Magento/GoogleAdwords/registration.php | 2 +- .../layout/checkout_onepage_success.xml | 2 +- .../view/frontend/templates/code.phtml | 2 +- app/code/Magento/GoogleAnalytics/Block/Ga.php | 16 +- .../Magento/GoogleAnalytics/Helper/Data.php | 16 +- ...nalyticsOnOrderSuccessPageViewObserver.php | 2 +- .../Test/Unit/Block/GaTest.php | 184 + .../Magento/GoogleAnalytics/composer.json | 2 +- app/code/Magento/GoogleAnalytics/etc/acl.xml | 2 +- .../GoogleAnalytics/etc/adminhtml/system.xml | 10 +- app/code/Magento/GoogleAnalytics/etc/di.xml | 2 +- .../GoogleAnalytics/etc/frontend/events.xml | 2 +- .../Magento/GoogleAnalytics/etc/module.xml | 2 +- .../Magento/GoogleAnalytics/registration.php | 2 +- .../view/frontend/layout/default.xml | 2 +- .../view/frontend/templates/ga.phtml | 2 +- .../GoogleOptimizer/Block/AbstractCode.php | 2 +- .../Block/Adminhtml/AbstractTab.php | 2 +- .../Catalog/Category/Edit/Googleoptimizer.php | 2 +- .../Category/Edit/GoogleoptimizerForm.php | 2 +- .../Product/Edit/Tab/Googleoptimizer.php | 2 +- .../Cms/Page/Edit/Tab/Googleoptimizer.php | 2 +- .../Adminhtml/Cms/Page/EntityCmsPage.php | 2 +- .../GoogleOptimizer/Block/Adminhtml/Form.php | 2 +- .../GoogleOptimizer/Block/Code/Category.php | 2 +- .../GoogleOptimizer/Block/Code/Page.php | 2 +- .../GoogleOptimizer/Block/Code/Product.php | 2 +- .../Magento/GoogleOptimizer/Helper/Code.php | 2 +- .../Magento/GoogleOptimizer/Helper/Data.php | 2 +- .../Magento/GoogleOptimizer/Helper/Form.php | 2 +- .../Magento/GoogleOptimizer/Model/Code.php | 2 +- .../Plugin/Catalog/Category/DataProvider.php | 2 +- .../Catalog/Product/Category/DataProvider.php | 2 +- .../Model/Plugin/Cms/Page/DataProvider.php | 2 +- .../Model/ResourceModel/Code.php | 2 +- .../GoogleOptimizer/Observer/AbstractSave.php | 2 +- ...CategoryGoogleExperimentScriptObserver.php | 2 +- .../SaveGoogleExperimentScriptObserver.php | 2 +- ...eleteCmsGoogleExperimentScriptObserver.php | 2 +- .../SaveGoogleExperimentScriptObserver.php | 2 +- ...eProductGoogleExperimentScriptObserver.php | 2 +- .../SaveGoogleExperimentScriptObserver.php | 2 +- .../GoogleOptimizer/Setup/InstallSchema.php | 2 +- .../Test/Unit/Block/Code/CategoryTest.php | 2 +- .../Test/Unit/Block/Code/ProductTest.php | 2 +- .../Test/Unit/Helper/CodeTest.php | 2 +- .../Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Helper/FormTest.php | 2 +- .../Product/Category/DataProviderTest.php | 2 +- ...goryGoogleExperimentScriptObserverTest.php | 2 +- ...SaveGoogleExperimentScriptObserverTest.php | 2 +- ...eCmsGoogleExperimentScriptObserverTest.php | 2 +- ...SaveGoogleExperimentScriptObserverTest.php | 2 +- ...ductGoogleExperimentScriptObserverTest.php | 2 +- ...SaveGoogleExperimentScriptObserverTest.php | 2 +- .../Form/Modifier/GoogleOptimizerTest.php | 2 +- .../Product/Form/Modifier/GoogleOptimizer.php | 2 +- .../Magento/GoogleOptimizer/composer.json | 2 +- .../GoogleOptimizer/etc/adminhtml/di.xml | 2 +- .../GoogleOptimizer/etc/adminhtml/system.xml | 2 +- .../Magento/GoogleOptimizer/etc/config.xml | 2 +- .../Magento/GoogleOptimizer/etc/events.xml | 2 +- .../Magento/GoogleOptimizer/etc/module.xml | 2 +- .../Magento/GoogleOptimizer/registration.php | 2 +- .../adminhtml/layout/catalog_product_new.xml | 2 +- .../view/adminhtml/layout/cms_page_edit.xml | 2 +- .../adminhtml/ui_component/category_form.xml | 2 +- .../adminhtml/ui_component/cms_page_form.xml | 2 +- .../ui_component/new_category_form.xml | 2 +- .../frontend/layout/catalog_category_view.xml | 2 +- .../frontend/layout/catalog_product_view.xml | 2 +- .../view/frontend/layout/cms_page_view.xml | 2 +- .../Model/Export/Product/Type/Grouped.php | 2 +- .../Model/Export/RowCustomizer.php | 2 +- .../Model/Import/Product/Type/Grouped.php | 2 +- .../Import/Product/Type/Grouped/Links.php | 2 +- .../Export/Product/RowCustomizerTest.php | 2 +- .../Import/Product/Type/Grouped/LinksTest.php | 2 +- .../Model/Import/Product/Type/GroupedTest.php | 2 +- .../Magento/GroupedImportExport/composer.json | 2 +- .../Magento/GroupedImportExport/etc/di.xml | 2 +- .../GroupedImportExport/etc/export.xml | 2 +- .../GroupedImportExport/etc/import.xml | 2 +- .../GroupedImportExport/etc/module.xml | 2 +- .../GroupedImportExport/registration.php | 2 +- .../Adminhtml/Items/Column/Name/Grouped.php | 2 +- .../Block/Adminhtml/Order/Create/Sidebar.php | 2 +- .../Product/Composite/Fieldset/Grouped.php | 2 +- .../Block/Cart/Item/Renderer/Grouped.php | 2 +- .../Block/Order/Email/Items/Order/Grouped.php | 2 +- .../Block/Order/Item/Renderer/Grouped.php | 2 +- .../Product/Grouped/AssociatedProducts.php | 2 +- .../ListAssociatedProducts.php | 2 +- .../Block/Product/View/Type/Grouped.php | 2 +- .../Block/Stockqty/Type/Grouped.php | 2 +- .../Controller/Adminhtml/Edit/Popup.php | 2 +- .../CustomerData/GroupedItem.php | 2 +- .../Product/Configuration/Plugin/Grouped.php | 2 +- .../Order/Pdf/Items/Creditmemo/Grouped.php | 2 +- .../Model/Order/Pdf/Items/Invoice/Grouped.php | 2 +- .../Cart/Configuration/Plugin/Grouped.php | 2 +- .../Model/Product/CatalogPrice.php | 2 +- .../Model/Product/CopyConstructor/Grouped.php | 2 +- .../Helper/ProductLinks/Plugin/Grouped.php | 2 +- .../Link/CollectionProvider/Grouped.php | 2 +- .../Product/Link/ProductEntity/Converter.php | 2 +- .../Model/Product/Type/Grouped.php | 16 +- .../Model/Product/Type/Grouped/Backend.php | 2 +- .../Model/Product/Type/Grouped/Price.php | 2 +- .../Model/Product/Type/Plugin.php | 2 +- .../ResourceModel/Indexer/Stock/Grouped.php | 2 +- .../Product/Indexer/Price/Grouped.php | 2 +- .../Indexer/Price/GroupedInterface.php | 2 +- .../Model/ResourceModel/Product/Link.php | 2 +- .../Product/Link/RelationPersister.php | 2 +- .../Grouped/AssociatedProductsCollection.php | 2 +- .../Product/Quote/Plugin/Initializer.php | 2 +- .../Pricing/Price/ConfiguredPrice.php | 2 +- .../Pricing/Price/FinalPrice.php | 2 +- .../GroupedProduct/Setup/InstallData.php | 2 +- .../GroupedProduct/Setup/UpgradeData.php | 2 +- .../Adminhtml/Order/Create/SidebarTest.php | 2 +- .../Composite/Fieldset/GroupedTest.php | 2 +- .../Block/Cart/Item/Renderer/GroupedTest.php | 2 +- .../ListAssociatedProductsTest.php | 2 +- .../Grouped/AssociatedProductsTest.php | 2 +- .../Block/Product/View/Type/GroupedTest.php | 2 +- .../Unit/Block/Stockqty/Type/GroupedTest.php | 2 +- .../Controller/Adminhtml/Edit/PopupTest.php | 2 +- .../Configuration/Plugin/GroupedTest.php | 2 +- .../Cart/Configuration/Plugin/GroupedTest.php | 2 +- .../Unit/Model/Product/CatalogPriceTest.php | 2 +- .../Product/CopyConstructor/GroupedTest.php | 2 +- .../ProductLinks/Plugin/GroupedTest.php | 2 +- .../Model/Product/Type/Grouped/PriceTest.php | 2 +- .../Unit/Model/Product/Type/GroupedTest.php | 25 +- .../Unit/Model/Product/Type/PluginTest.php | 2 +- .../Test/Unit/Model/ProductTest.php | 2 +- .../Product/Link/RelationPersisterTest.php | 2 +- .../Product/Quote/Plugin/InitializerTest.php | 2 +- .../Pricing/Price/ConfiguredPriceTest.php | 2 +- .../Unit/Pricing/Price/FinalPriceTest.php | 2 +- .../Form/Modifier/CustomOptionsTest.php | 2 +- .../Product/Form/Modifier/GroupedTest.php | 2 +- .../GroupedProductDataProviderTest.php | 2 +- .../Product/Form/Modifier/CustomOptions.php | 2 +- .../Product/Form/Modifier/Grouped.php | 2 +- .../Product/Form/Modifier/StockData.php | 2 +- .../Product/GroupedProductDataProvider.php | 2 +- app/code/Magento/GroupedProduct/composer.json | 2 +- .../GroupedProduct/etc/adminhtml/di.xml | 2 +- .../GroupedProduct/etc/adminhtml/routes.xml | 2 +- .../GroupedProduct/etc/adminhtml/system.xml | 2 +- .../Magento/GroupedProduct/etc/config.xml | 2 +- app/code/Magento/GroupedProduct/etc/di.xml | 2 +- .../etc/extension_attributes.xml | 2 +- .../GroupedProduct/etc/frontend/di.xml | 2 +- .../Magento/GroupedProduct/etc/module.xml | 2 +- app/code/Magento/GroupedProduct/etc/pdf.xml | 2 +- .../GroupedProduct/etc/product_types.xml | 2 +- app/code/Magento/GroupedProduct/etc/sales.xml | 2 +- .../Magento/GroupedProduct/registration.php | 2 +- .../layout/catalog_product_grouped.xml | 2 +- .../adminhtml/layout/catalog_product_new.xml | 2 +- .../catalog_product_view_type_grouped.xml | 2 +- .../layout/groupedproduct_edit_popup.xml | 2 +- .../layout/groupedproduct_popup_grid.xml | 2 +- .../layout/sales_order_creditmemo_new.xml | 2 +- .../sales_order_creditmemo_updateqty.xml | 2 +- .../layout/sales_order_creditmemo_view.xml | 2 +- .../layout/sales_order_invoice_new.xml | 2 +- .../layout/sales_order_invoice_updateqty.xml | 2 +- .../layout/sales_order_invoice_view.xml | 2 +- .../adminhtml/layout/sales_order_view.xml | 2 +- .../view/adminhtml/requirejs-config.js | 4 +- .../product/composite/fieldset/grouped.phtml | 2 +- .../templates/product/grouped/grouped.phtml | 2 +- .../templates/product/grouped/list.phtml | 2 +- .../templates/product/stock/disabler.phtml | 2 +- .../ui_component/grouped_product_listing.xml | 2 +- .../adminhtml/web/css/grouped-product.css | 2 +- .../view/adminhtml/web/js/grouped-product.js | 51 +- .../base/layout/catalog_product_prices.xml | 2 +- .../templates/product/price/final_price.phtml | 2 +- ...catalog_product_rss_feed_renderer_list.xml | 2 +- .../catalog_product_view_type_grouped.xml | 2 +- .../layout/checkout_cart_item_renderers.xml | 2 +- ...checkout_onepage_review_item_renderers.xml | 2 +- ...sales_email_order_creditmemo_renderers.xml | 2 +- .../sales_email_order_invoice_renderers.xml | 2 +- .../layout/sales_email_order_renderers.xml | 2 +- .../frontend/layout/sales_guest_invoice.xml | 2 +- .../sales_order_creditmemo_renderers.xml | 2 +- .../layout/sales_order_invoice_renderers.xml | 2 +- .../layout/sales_order_item_renderers.xml | 2 +- ...sales_order_print_creditmemo_renderers.xml | 2 +- .../sales_order_print_invoice_renderers.xml | 2 +- .../layout/sales_order_print_renderers.xml | 2 +- .../templates/product/view/type/default.phtml | 2 +- .../templates/product/view/type/grouped.phtml | 6 +- .../Block/Adminhtml/Export/Edit.php | 2 +- .../Block/Adminhtml/Export/Edit/Form.php | 12 +- .../Block/Adminhtml/Export/Filter.php | 55 +- .../Block/Adminhtml/Form/After.php | 2 +- .../Grid/Column/Renderer/Download.php | 2 +- .../Adminhtml/Grid/Column/Renderer/Error.php | 2 +- .../ImportExport/Block/Adminhtml/History.php | 2 +- .../Block/Adminhtml/Import/Edit.php | 2 +- .../Block/Adminhtml/Import/Edit/Before.php | 2 +- .../Block/Adminhtml/Import/Edit/Form.php | 12 +- .../Block/Adminhtml/Import/Frame/Result.php | 2 +- .../Controller/Adminhtml/Export.php | 2 +- .../Controller/Adminhtml/Export/Export.php | 2 +- .../Controller/Adminhtml/Export/GetFilter.php | 2 +- .../Controller/Adminhtml/Export/Index.php | 2 +- .../Controller/Adminhtml/History.php | 2 +- .../Controller/Adminhtml/History/Download.php | 2 +- .../Controller/Adminhtml/History/Index.php | 2 +- .../Controller/Adminhtml/Import.php | 2 +- .../Controller/Adminhtml/Import/Download.php | 2 +- .../Controller/Adminhtml/Import/Index.php | 2 +- .../Controller/Adminhtml/Import/Start.php | 2 +- .../Controller/Adminhtml/Import/Validate.php | 2 +- .../Controller/Adminhtml/ImportResult.php | 2 +- .../Files/Sample/advanced_pricing.csv | 10 +- app/code/Magento/ImportExport/Helper/Data.php | 2 +- .../Magento/ImportExport/Helper/Report.php | 2 +- .../ImportExport/Model/AbstractModel.php | 2 +- .../Magento/ImportExport/Model/Export.php | 7 +- .../Model/Export/AbstractEntity.php | 2 +- .../Model/Export/Adapter/AbstractAdapter.php | 2 +- .../ImportExport/Model/Export/Adapter/Csv.php | 2 +- .../Model/Export/Adapter/Factory.php | 2 +- .../ImportExport/Model/Export/Config.php | 19 +- .../Model/Export/Config/Converter.php | 2 +- .../Model/Export/Config/Reader.php | 2 +- .../Model/Export/Config/SchemaLocator.php | 2 +- .../Model/Export/ConfigInterface.php | 2 +- .../Model/Export/Entity/AbstractEav.php | 2 +- .../Model/Export/Entity/AbstractEntity.php | 2 +- .../Model/Export/Entity/Factory.php | 2 +- .../ImportExport/Model/Export/Factory.php | 2 +- .../Magento/ImportExport/Model/History.php | 2 +- .../Magento/ImportExport/Model/Import.php | 12 +- .../Model/Import/AbstractEntity.php | 2 +- .../Model/Import/AbstractSource.php | 2 +- .../ImportExport/Model/Import/Adapter.php | 2 +- .../ImportExport/Model/Import/Config.php | 19 +- .../Model/Import/Config/Converter.php | 2 +- .../Model/Import/Config/Reader.php | 2 +- .../Model/Import/Config/SchemaLocator.php | 2 +- .../Model/Import/ConfigInterface.php | 2 +- .../Model/Import/Entity/AbstractEav.php | 4 +- .../Model/Import/Entity/AbstractEntity.php | 2 +- .../Model/Import/Entity/Factory.php | 2 +- .../ErrorProcessing/ProcessingError.php | 2 +- .../ProcessingErrorAggregator.php | 2 +- .../ProcessingErrorAggregatorInterface.php | 2 +- .../ImportExport/Model/Import/Source/Csv.php | 2 +- .../ImportExport/Model/Import/Source/Zip.php | 2 +- .../Magento/ImportExport/Model/Report/Csv.php | 2 +- .../Model/Report/ReportProcessorInterface.php | 2 +- .../CollectionByPagesIterator.php | 2 +- .../Model/ResourceModel/Helper.php | 2 +- .../Model/ResourceModel/History.php | 2 +- .../ResourceModel/History/Collection.php | 2 +- .../Model/ResourceModel/Import/Data.php | 2 +- .../Model/Source/Export/Entity.php | 2 +- .../Model/Source/Export/Format.php | 2 +- .../Model/Source/Import/AbstractBehavior.php | 2 +- .../Model/Source/Import/Behavior/Basic.php | 2 +- .../Model/Source/Import/Behavior/Custom.php | 2 +- .../Model/Source/Import/Behavior/Factory.php | 2 +- .../Model/Source/Import/Entity.php | 2 +- .../ImportExport/Setup/InstallSchema.php | 2 +- .../ImportExport/Setup/UpgradeSchema.php | 2 +- .../Block/Adminhtml/Export/FilterTest.php | 2 +- .../Grid/Column/Renderer/DownloadTest.php | 2 +- .../Block/Adminhtml/Import/Edit/FormTest.php | 2 +- .../Adminhtml/History/DownloadTest.php | 2 +- .../Adminhtml/History/IndexTest.php | 2 +- .../Test/Unit/Helper/ReportTest.php | 2 +- .../Model/Export/Config/ConverterTest.php | 2 +- .../Model/Export/Config/SchemaLocatorTest.php | 2 +- .../Test/Unit/Model/Export/Config/XsdTest.php | 2 +- .../Model/Export/Config/_files/export.php | 2 +- .../Model/Export/Config/_files/export.xml | 2 +- .../Config/_files/export_merged_valid.xml | 2 +- .../Export/Config/_files/export_valid.xml | 2 +- .../_files/invalidExportMergedXmlArray.php | 2 +- .../Config/_files/invalidExportXmlArray.php | 2 +- .../Test/Unit/Model/Export/ConfigTest.php | 75 +- .../Model/Export/Entity/AbstractEavTest.php | 2 +- .../Unit/Model/Export/EntityAbstractTest.php | 2 +- .../Test/Unit/Model/ExportTest.php | 2 +- .../Model/Import/AbstractImportTestCase.php | 2 +- .../Test/Unit/Model/Import/AdapterTest.php | 2 +- .../Model/Import/Config/ConverterTest.php | 2 +- .../Model/Import/Config/SchemaLocatorTest.php | 2 +- .../Model/Import/Config/XsdMergedTest.php | 2 +- .../Test/Unit/Model/Import/Config/XsdTest.php | 2 +- .../Model/Import/Config/_files/import.php | 2 +- .../Model/Import/Config/_files/import.xml | 2 +- .../_files/invalidImportMergedXmlArray.php | 2 +- .../Config/_files/invalidImportXmlArray.php | 2 +- .../Import/Config/_files/valid_import.xml | 2 +- .../Config/_files/valid_import_merged.xml | 2 +- .../Test/Unit/Model/Import/ConfigTest.php | 58 +- .../Unit/Model/Import/Entity/AbstractTest.php | 2 +- .../Model/Import/Entity/EavAbstractTest.php | 2 +- .../Unit/Model/Import/EntityAbstractTest.php | 2 +- .../ProcessingErrorAggregatorTest.php | 2 +- .../ErrorProcessing/ProcessingErrorTest.php | 2 +- .../Test/Unit/Model/Import/Source/CsvTest.php | 2 +- .../Test/Unit/Model/Import/Source/ZipTest.php | 2 +- .../Unit/Model/Import/SourceAbstractTest.php | 2 +- .../Test/Unit/Model/ImportTest.php | 93 +- .../Test/Unit/Model/Report/CsvTest.php | 2 +- .../CollectionByPagesIteratorTest.php | 2 +- .../Unit/Model/ResourceModel/HistoryTest.php | 2 +- .../Import/AbstractBehaviorTestCase.php | 2 +- .../Source/Import/Behavior/BasicTest.php | 2 +- .../Source/Import/Behavior/CustomTest.php | 2 +- .../Source/Import/BehaviorAbstractTest.php | 2 +- app/code/Magento/ImportExport/composer.json | 3 +- app/code/Magento/ImportExport/etc/acl.xml | 2 +- .../ImportExport/etc/adminhtml/menu.xml | 2 +- .../ImportExport/etc/adminhtml/routes.xml | 2 +- app/code/Magento/ImportExport/etc/config.xml | 2 +- app/code/Magento/ImportExport/etc/di.xml | 2 +- app/code/Magento/ImportExport/etc/export.xsd | 2 +- .../ImportExport/etc/export_merged.xsd | 2 +- app/code/Magento/ImportExport/etc/import.xsd | 2 +- .../ImportExport/etc/import_merged.xsd | 2 +- app/code/Magento/ImportExport/etc/module.xml | 2 +- .../Magento/ImportExport/registration.php | 2 +- .../layout/adminhtml_export_getfilter.xml | 2 +- .../layout/adminhtml_export_index.xml | 2 +- .../layout/adminhtml_history_grid_block.xml | 2 +- .../layout/adminhtml_history_index.xml | 2 +- .../layout/adminhtml_import_busy.xml | 2 +- .../layout/adminhtml_import_index.xml | 2 +- .../layout/adminhtml_import_start.xml | 2 +- .../layout/adminhtml_import_validate.xml | 2 +- .../view/adminhtml/templates/busy.phtml | 2 +- .../templates/export/form/after.phtml | 2 +- .../templates/export/form/before.phtml | 5 +- .../templates/export/form/filter/after.phtml | 2 +- .../templates/import/form/after.phtml | 2 +- .../templates/import/form/before.phtml | 2 +- .../templates/import/frame/result.phtml | 2 +- .../view/adminhtml/web/css/importexport.css | 2 +- app/code/Magento/Indexer/App/Indexer.php | 2 +- .../Indexer/Block/Backend/Container.php | 2 +- .../Grid/Column/Renderer/Scheduled.php | 2 +- .../Backend/Grid/Column/Renderer/Status.php | 2 +- .../Backend/Grid/Column/Renderer/Updated.php | 2 +- .../Block/Backend/Grid/ItemsUpdater.php | 2 +- .../Command/AbstractIndexerCommand.php | 2 +- .../Command/AbstractIndexerManageCommand.php | 2 +- .../Console/Command/IndexerInfoCommand.php | 2 +- .../Console/Command/IndexerReindexCommand.php | 2 +- .../Command/IndexerResetStateCommand.php | 2 +- .../Console/Command/IndexerSetModeCommand.php | 2 +- .../Command/IndexerShowModeCommand.php | 2 +- .../Console/Command/IndexerStatusCommand.php | 2 +- .../Indexer/Controller/Adminhtml/Indexer.php | 2 +- .../Adminhtml/Indexer/ListAction.php | 2 +- .../Adminhtml/Indexer/MassChangelog.php | 2 +- .../Adminhtml/Indexer/MassOnTheFly.php | 2 +- .../Magento/Indexer/Cron/ClearChangelog.php | 2 +- .../Indexer/Cron/ReindexAllInvalid.php | 2 +- app/code/Magento/Indexer/Cron/UpdateMview.php | 2 +- app/code/Magento/Indexer/Model/Config.php | 2 +- .../Magento/Indexer/Model/Config/Data.php | 18 +- app/code/Magento/Indexer/Model/Indexer.php | 2 +- .../Indexer/Model/Indexer/Collection.php | 2 +- .../Magento/Indexer/Model/Indexer/State.php | 2 +- .../Magento/Indexer/Model/Message/Invalid.php | 2 +- .../Indexer/Model/Mview/View/State.php | 2 +- app/code/Magento/Indexer/Model/Processor.php | 2 +- .../Indexer/Model/Processor/CleanCache.php | 2 +- .../Indexer/Model/Processor/Handler.php | 2 +- .../Model/ResourceModel/AbstractResource.php | 2 +- .../Model/ResourceModel/Indexer/State.php | 2 +- .../Indexer/State/Collection.php | 2 +- .../Model/ResourceModel/Mview/View/State.php | 2 +- .../Mview/View/State/Collection.php | 2 +- .../Indexer/Model/Source/DataInterface.php | 2 +- .../Indexer/Model/Source/ServiceSource.php | 2 +- .../Magento/Indexer/Setup/InstallData.php | 2 +- .../Magento/Indexer/Setup/InstallSchema.php | 2 +- app/code/Magento/Indexer/Setup/Recurring.php | 2 +- .../Magento/Indexer/Setup/RecurringData.php | 56 + .../Indexer/Test/Unit/App/IndexerTest.php | 2 +- .../Test/Unit/Block/Backend/ContainerTest.php | 2 +- .../Grid/Column/Renderer/ScheduledTest.php | 2 +- .../Grid/Column/Renderer/StatusTest.php | 2 +- .../Grid/Column/Renderer/UpdatedTest.php | 2 +- .../Block/Backend/Grid/ItemsUpdaterTest.php | 2 +- .../AbstractIndexerCommandCommonSetup.php | 2 +- .../Command/IndexerInfoCommandTest.php | 2 +- .../Command/IndexerReindexCommandTest.php | 2 +- .../Command/IndexerResetStateCommandTest.php | 2 +- .../Command/IndexerSetModeCommandTest.php | 2 +- .../Command/IndexerShowModeCommandTest.php | 2 +- .../Command/IndexerStatusCommandTest.php | 2 +- .../Adminhtml/Indexer/ListActionTest.php | 2 +- .../Adminhtml/Indexer/MassChangelogTest.php | 2 +- .../Adminhtml/Indexer/MassOnTheFlyTest.php | 2 +- .../Test/Unit/Model/CacheContextTest.php | 2 +- .../Test/Unit/Model/Config/DataTest.php | 33 +- .../Indexer/Test/Unit/Model/ConfigTest.php | 2 +- .../Model/Indexer/AbstractProcessorStub.php | 2 +- .../Model/Indexer/AbstractProcessorTest.php | 2 +- .../Unit/Model/Indexer/CollectionTest.php | 2 +- .../Test/Unit/Model/Indexer/StateTest.php | 2 +- .../Indexer/Test/Unit/Model/IndexerTest.php | 2 +- .../Test/Unit/Model/Message/InvalidTest.php | 2 +- .../Test/Unit/Model/Mview/View/StateTest.php | 2 +- .../Unit/Model/Processor/CleanCacheTest.php | 2 +- .../Indexer/Test/Unit/Model/ProcessorTest.php | 2 +- .../ResourceModel/AbstractResourceStub.php | 2 +- .../ResourceModel/AbstractResourceTest.php | 2 +- .../Indexer/State/CollectionTest.php | 2 +- .../Model/ResourceModel/Indexer/StateTest.php | 2 +- .../Mview/View/State/CollectionTest.php | 2 +- .../ResourceModel/Mview/View/StateTest.php | 2 +- app/code/Magento/Indexer/composer.json | 2 +- app/code/Magento/Indexer/etc/acl.xml | 2 +- app/code/Magento/Indexer/etc/adminhtml/di.xml | 2 +- .../Magento/Indexer/etc/adminhtml/menu.xml | 2 +- .../Magento/Indexer/etc/adminhtml/routes.xml | 2 +- app/code/Magento/Indexer/etc/cron_groups.xml | 4 +- app/code/Magento/Indexer/etc/crontab.xml | 2 +- app/code/Magento/Indexer/etc/di.xml | 2 +- app/code/Magento/Indexer/etc/module.xml | 2 +- app/code/Magento/Indexer/registration.php | 2 +- .../adminhtml/layout/indexer_indexer_list.xml | 2 +- .../layout/indexer_indexer_list_grid.xml | 4 +- .../Api/AdminTokenServiceInterface.php | 2 +- .../Api/AuthorizationServiceInterface.php | 2 +- .../Api/CustomerTokenServiceInterface.php | 2 +- .../Api/IntegrationServiceInterface.php | 2 +- .../Integration/Api/OauthServiceInterface.php | 2 +- .../Block/Adminhtml/Integration.php | 2 +- .../Activate/Permissions/Tab/Webapi.php | 2 +- .../Integration/Activate/Permissions/Tabs.php | 2 +- .../Block/Adminhtml/Integration/Edit.php | 2 +- .../Block/Adminhtml/Integration/Edit/Form.php | 2 +- .../Adminhtml/Integration/Edit/Tab/Info.php | 2 +- .../Adminhtml/Integration/Edit/Tab/Webapi.php | 2 +- .../Block/Adminhtml/Integration/Edit/Tabs.php | 2 +- .../Block/Adminhtml/Integration/Grid.php | 2 +- .../Block/Adminhtml/Integration/Tokens.php | 2 +- .../Widget/Grid/Column/Renderer/Button.php | 2 +- .../Grid/Column/Renderer/Button/Delete.php | 2 +- .../Grid/Column/Renderer/Button/Edit.php | 2 +- .../Widget/Grid/Column/Renderer/Link.php | 2 +- .../Grid/Column/Renderer/Link/Activate.php | 2 +- .../Widget/Grid/Column/Renderer/Name.php | 2 +- .../Controller/Adminhtml/Integration.php | 2 +- .../Adminhtml/Integration/Delete.php | 2 +- .../Controller/Adminhtml/Integration/Edit.php | 2 +- .../Controller/Adminhtml/Integration/Grid.php | 2 +- .../Adminhtml/Integration/Index.php | 2 +- .../Integration/LoginSuccessCallback.php | 2 +- .../Adminhtml/Integration/NewAction.php | 2 +- .../Integration/PermissionsDialog.php | 2 +- .../Controller/Adminhtml/Integration/Save.php | 2 +- .../Adminhtml/Integration/TokensDialog.php | 2 +- .../Adminhtml/Integration/TokensExchange.php | 2 +- .../Integration/Controller/Token/Access.php | 2 +- .../Integration/Controller/Token/Request.php | 2 +- .../CleanExpiredAuthenticationFailures.php | 2 +- app/code/Magento/Integration/Helper/Data.php | 2 +- .../Magento/Integration/Helper/Oauth/Data.php | 2 +- .../Integration/Model/AdminTokenService.php | 2 +- .../Model/AuthorizationService.php | 2 +- .../Magento/Integration/Model/Cache/Type.php | 2 +- .../Model/Cache/TypeConsolidated.php | 2 +- .../Model/Cache/TypeIntegration.php | 2 +- app/code/Magento/Integration/Model/Config.php | 26 +- .../Model/Config/Consolidated/Converter.php | 2 +- .../Model/Config/Consolidated/Reader.php | 2 +- .../Config/Consolidated/SchemaLocator.php | 2 +- .../Integration/Model/Config/Converter.php | 2 +- .../Model/Config/Integration/Converter.php | 2 +- .../Model/Config/Integration/Reader.php | 2 +- .../Config/Integration/SchemaLocator.php | 2 +- .../Integration/Model/Config/Reader.php | 2 +- .../Model/Config/SchemaLocator.php | 2 +- .../Model/ConfigBasedIntegrationManager.php | 2 +- .../Integration/Model/ConsolidatedConfig.php | 22 +- .../Model/CredentialsValidator.php | 2 +- .../Model/CustomerTokenService.php | 2 +- .../Magento/Integration/Model/Integration.php | 2 +- .../Model/Integration/Source/Status.php | 2 +- .../Integration/Model/IntegrationConfig.php | 22 +- .../Integration/Model/IntegrationService.php | 2 +- .../Model/Message/RecreatedIntegration.php | 2 +- .../Integration/Model/Oauth/Consumer.php | 2 +- .../Oauth/Consumer/Validator/KeyLength.php | 2 +- .../Magento/Integration/Model/Oauth/Nonce.php | 2 +- .../Model/Oauth/Nonce/Generator.php | 2 +- .../Magento/Integration/Model/Oauth/Token.php | 2 +- .../Model/Oauth/Token/Provider.php | 2 +- .../Model/Oauth/Token/RequestLog/Config.php | 2 +- .../Token/RequestLog/ReaderInterface.php | 2 +- .../Token/RequestLog/WriterInterface.php | 2 +- .../Model/Oauth/Token/RequestThrottler.php | 2 +- .../Integration/Model/OauthService.php | 2 +- .../Integration/Model/Plugin/Integration.php | 2 +- .../Model/ResourceModel/Integration.php | 2 +- .../ResourceModel/Integration/Collection.php | 2 +- .../Model/ResourceModel/Oauth/Consumer.php | 2 +- .../Oauth/Consumer/Collection.php | 2 +- .../Model/ResourceModel/Oauth/Nonce.php | 2 +- .../ResourceModel/Oauth/Nonce/Collection.php | 2 +- .../Model/ResourceModel/Oauth/Token.php | 2 +- .../ResourceModel/Oauth/Token/Collection.php | 2 +- .../ResourceModel/Oauth/Token/RequestLog.php | 2 +- .../Integration/Setup/InstallSchema.php | 2 +- .../Magento/Integration/Setup/Recurring.php | 2 +- .../Integration/Setup/UpgradeSchema.php | 2 +- .../Integration/Edit/Tab/InfoTest.php | 2 +- .../Integration/Edit/Tab/WebapiTest.php | 2 +- .../Grid/Column/Renderer/ButtonTest.php | 2 +- .../Widget/Grid/Column/Renderer/LinkTest.php | 2 +- .../Widget/Grid/Column/Renderer/NameTest.php | 2 +- .../Adminhtml/Integration/DeleteTest.php | 2 +- .../Adminhtml/Integration/EditTest.php | 2 +- .../Adminhtml/Integration/IndexTest.php | 2 +- .../Adminhtml/Integration/NewActionTest.php | 2 +- .../Integration/PermissionsDialogTest.php | 2 +- .../Adminhtml/Integration/SaveTest.php | 2 +- .../Integration/TokensDialogTest.php | 2 +- .../Controller/Adminhtml/IntegrationTest.php | 2 +- .../Test/Unit/Controller/Token/AccessTest.php | 2 +- .../Unit/Controller/Token/RequestTest.php | 2 +- .../Integration/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Helper/Oauth/ConsumerTest.php | 2 +- .../Test/Unit/Helper/Oauth/DataTest.php | 2 +- .../Test/Unit/Helper/Oauth/OauthTest.php | 2 +- .../Test/Unit/Helper/_files/acl-map.php | 2 +- .../Test/Unit/Helper/_files/acl.php | 2 +- .../Test/Unit/Model/AdminTokenServiceTest.php | 2 +- .../Unit/Model/AuthorizationServiceTest.php | 2 +- .../Config/Consolidated/ConverterTest.php | 2 +- .../Config/Consolidated/SchemaLocatorTest.php | 2 +- .../Model/Config/Consolidated/XsdTest.php | 2 +- .../Model/Config/Consolidated/_files/acl.php | 2 +- .../Consolidated/_files/integration.php | 2 +- .../Consolidated/_files/integration.xml | 2 +- .../Test/Unit/Model/Config/ConverterTest.php | 2 +- .../Config/Integration/ConverterTest.php | 2 +- .../Config/Integration/SchemaLocatorTest.php | 2 +- .../Unit/Model/Config/Integration/XsdTest.php | 2 +- .../Model/Config/Integration/_files/api.php | 2 +- .../Model/Config/Integration/_files/api.xml | 2 +- .../Unit/Model/Config/SchemaLocatorTest.php | 2 +- .../Test/Unit/Model/Config/XsdTest.php | 2 +- .../Test/Unit/Model/Config/_files/config.xml | 2 +- .../Unit/Model/Config/_files/integration.php | 2 +- .../Unit/Model/ConsolidatedConfigTest.php | 39 +- .../Unit/Model/CredentialsValidatorTest.php | 2 +- .../Unit/Model/CustomerTokenServiceTest.php | 2 +- .../Model/Integration/Source/StatusTest.php | 2 +- .../Test/Unit/Model/IntegrationConfigTest.php | 40 +- .../Unit/Model/IntegrationServiceTest.php | 2 +- .../Test/Unit/Model/IntegrationTest.php | 2 +- .../Test/Unit/Model/ManagerTest.php | 2 +- .../Consumer/Validator/KeyLengthTest.php | 2 +- .../Test/Unit/Model/Oauth/ConsumerTest.php | 2 +- .../Test/Unit/Model/Oauth/NonceTest.php | 2 +- .../Unit/Model/Oauth/Token/ProviderTest.php | 2 +- .../Test/Unit/Model/Oauth/TokenTest.php | 2 +- .../Test/Unit/Model/OauthServiceTest.php | 2 +- .../Unit/Model/Plugin/IntegrationTest.php | 2 +- .../Integration/CollectionTest.php | 2 +- .../Model/ResourceModel/IntegrationTest.php | 2 +- .../ResourceModel/Oauth/ConsumerTest.php | 2 +- .../Model/ResourceModel/Oauth/NonceTest.php | 2 +- .../Oauth/Token/CollectionTest.php | 2 +- .../Model/ResourceModel/Oauth/TokenTest.php | 2 +- .../Integration/Test/Unit/Oauth/OauthTest.php | 2 +- app/code/Magento/Integration/composer.json | 2 +- .../Magento/Integration/etc/adminhtml/di.xml | 2 +- .../Integration/etc/adminhtml/menu.xml | 2 +- .../Integration/etc/adminhtml/routes.xml | 2 +- .../Integration/etc/adminhtml/system.xml | 2 +- app/code/Magento/Integration/etc/cache.xml | 2 +- app/code/Magento/Integration/etc/config.xml | 2 +- app/code/Magento/Integration/etc/crontab.xml | 2 +- app/code/Magento/Integration/etc/di.xml | 2 +- .../Integration/etc/frontend/routes.xml | 2 +- .../Integration/etc/integration/api.xsd | 4 +- .../Integration/etc/integration/config.xsd | 4 +- .../etc/integration/integration.xsd | 2 +- .../etc/integration/integration_base.xsd | 2 +- .../etc/integration/integration_file.xsd | 2 +- app/code/Magento/Integration/etc/module.xml | 2 +- app/code/Magento/Integration/etc/webapi.xml | 2 +- app/code/Magento/Integration/registration.php | 2 +- .../layout/adminhtml_integration_edit.xml | 2 +- .../layout/adminhtml_integration_grid.xml | 2 +- .../adminhtml_integration_grid_block.xml | 2 +- .../layout/adminhtml_integration_index.xml | 2 +- .../layout/adminhtml_integration_new.xml | 2 +- ...dminhtml_integration_permissionsdialog.xml | 2 +- .../adminhtml_integration_tokensdialog.xml | 2 +- .../adminhtml_integration_tokensexchange.xml | 2 +- .../view/adminhtml/requirejs-config.js | 6 +- .../integration/activate/permissions.phtml | 2 +- .../activate/permissions/tab/webapi.phtml | 2 +- .../integration/popup_container.phtml | 2 +- .../integration/tokens_exchange.phtml | 2 +- .../adminhtml/templates/resourcetree.phtml | 2 +- .../view/adminhtml/web/js/integration.js | 185 +- .../LayeredNavigation/Block/Navigation.php | 2 +- .../Block/Navigation/FilterRenderer.php | 2 +- .../Navigation/FilterRendererInterface.php | 2 +- .../Block/Navigation/State.php | 2 +- .../Model/Aggregation/Status.php | 2 +- .../Attribute/Source/FilterableOptions.php | 2 +- ...ductAttributeFormBuildFrontTabObserver.php | 2 +- .../ProductAttributeGridBuildObserver.php | 2 +- .../Test/Unit/Block/NavigationTest.php | 2 +- .../Unit/Model/Aggregation/StatusTest.php | 2 +- .../Magento/LayeredNavigation/composer.json | 2 +- .../etc/adminhtml/events.xml | 4 +- .../etc/adminhtml/system.xml | 2 +- .../Magento/LayeredNavigation/etc/config.xml | 2 +- app/code/Magento/LayeredNavigation/etc/di.xml | 2 +- .../LayeredNavigation/etc/frontend/di.xml | 2 +- .../Magento/LayeredNavigation/etc/module.xml | 2 +- .../LayeredNavigation/registration.php | 2 +- .../product_attribute_add_form.xml | 2 +- .../ui_component/product_attributes_grid.xml | 2 +- .../product_attributes_listing.xml | 2 +- .../catalog_category_view_type_layered.xml | 2 +- ...ory_view_type_layered_without_children.xml | 2 +- .../layout/catalogsearch_result_index.xml | 2 +- .../view/frontend/page_layout/1column.xml | 2 +- .../frontend/page_layout/2columns-left.xml | 2 +- .../frontend/page_layout/2columns-right.xml | 2 +- .../view/frontend/page_layout/3columns.xml | 2 +- .../view/frontend/page_layout/empty.xml | 2 +- .../frontend/templates/layer/filter.phtml | 2 +- .../view/frontend/templates/layer/state.phtml | 2 +- .../view/frontend/templates/layer/view.phtml | 2 +- app/code/Magento/Marketplace/Block/Index.php | 2 +- .../Magento/Marketplace/Block/Partners.php | 2 +- .../Controller/Adminhtml/Index.php | 2 +- .../Controller/Adminhtml/Index/Index.php | 2 +- .../Controller/Adminhtml/Partners.php | 2 +- .../Controller/Adminhtml/Partners/Index.php | 2 +- app/code/Magento/Marketplace/Helper/Cache.php | 19 +- .../Magento/Marketplace/Model/Partners.php | 2 +- .../Test/Unit/Block/PartnersTest.php | 2 +- .../Test/Unit/Controller/Index/IndexTest.php | 2 +- .../Unit/Controller/Partners/IndexTest.php | 2 +- .../Test/Unit/Helper/CacheTest.php | 99 +- .../Test/Unit/Model/PartnersTest.php | 2 +- app/code/Magento/Marketplace/composer.json | 2 +- .../Marketplace/etc/adminhtml/menu.xml | 2 +- .../Marketplace/etc/adminhtml/routes.xml | 2 +- app/code/Magento/Marketplace/etc/module.xml | 2 +- app/code/Magento/Marketplace/registration.php | 2 +- .../layout/marketplace_index_index.xml | 2 +- .../layout/marketplace_partners_index.xml | 2 +- .../view/adminhtml/templates/index.phtml | 2 +- .../view/adminhtml/templates/partners.phtml | 2 +- .../Marketplace/view/adminhtml/web/default.js | 2 +- app/code/Magento/MediaStorage/App/Media.php | 2 +- .../System/Storage/Media/Synchronize.php | 2 +- .../System/Config/System/Storage.php | 2 +- .../System/Config/System/Storage/Status.php | 2 +- .../Config/System/Storage/Synchronize.php | 2 +- .../MediaStorage/Helper/File/Media.php | 2 +- .../MediaStorage/Helper/File/Storage.php | 2 +- .../Helper/File/Storage/Database.php | 3 +- .../Model/Asset/Plugin/CleanMergedJsCss.php | 2 +- .../Config/Backend/Storage/Media/Database.php | 2 +- .../Config/Source/Storage/Media/Database.php | 2 +- .../Config/Source/Storage/Media/Storage.php | 2 +- .../MediaStorage/Model/File/Storage.php | 2 +- .../Model/File/Storage/Config.php | 2 +- .../Model/File/Storage/Database.php | 2 +- .../Storage/Database/AbstractDatabase.php | 2 +- .../Model/File/Storage/Directory/Database.php | 2 +- .../MediaStorage/Model/File/Storage/File.php | 2 +- .../MediaStorage/Model/File/Storage/Flag.php | 2 +- .../Model/File/Storage/Request.php | 2 +- .../Model/File/Storage/Response.php | 2 +- .../Model/File/Storage/Synchronization.php | 2 +- .../MediaStorage/Model/File/Uploader.php | 2 +- .../Model/File/Validator/AvailablePath.php | 2 +- .../File/Validator/NotProtectedExtension.php | 2 +- .../File/Storage/AbstractStorage.php | 2 +- .../ResourceModel/File/Storage/Database.php | 2 +- .../File/Storage/Directory/Database.php | 2 +- .../Model/ResourceModel/File/Storage/File.php | 2 +- .../MediaStorage/Test/Unit/App/MediaTest.php | 2 +- .../Test/Unit/Helper/File/MediaTest.php | 2 +- .../Unit/Helper/File/Storage/DatabaseTest.php | 2 +- .../Test/Unit/Helper/File/StorageTest.php | 2 +- .../Asset/Plugin/CleanMergedJsCssTest.php | 2 +- .../Source/Storage/Media/DatabaseTest.php | 2 +- .../Unit/Model/File/Storage/ConfigTest.php | 2 +- .../File/Storage/Directory/DatabaseTest.php | 2 +- .../Unit/Model/File/Storage/MediaTest.php | 2 +- .../Unit/Model/File/Storage/RequestTest.php | 2 +- .../File/Storage/SynchronizationTest.php | 2 +- .../Unit/Model/File/Storage/_files/config.xml | 2 +- .../ResourceModel/File/Storage/FileTest.php | 2 +- app/code/Magento/MediaStorage/composer.json | 2 +- .../MediaStorage/etc/adminhtml/routes.xml | 4 +- .../MediaStorage/etc/adminhtml/system.xml | 2 +- app/code/Magento/MediaStorage/etc/di.xml | 4 +- app/code/Magento/MediaStorage/etc/module.xml | 2 +- .../Magento/MediaStorage/registration.php | 2 +- .../system/storage/media/synchronize.phtml | 2 +- .../Adminhtml/Product/Helper/Form/Type.php | 2 +- .../Product/Helper/Form/Type/Price.php | 2 +- app/code/Magento/Msrp/Block/Popup.php | 2 +- app/code/Magento/Msrp/Block/Total.php | 2 +- app/code/Magento/Msrp/Helper/Data.php | 2 +- app/code/Magento/Msrp/Model/Config.php | 2 +- app/code/Magento/Msrp/Model/Msrp.php | 2 +- .../Model/Product/Attribute/Source/Type.php | 2 +- .../Product/Attribute/Source/Type/Price.php | 2 +- .../Magento/Msrp/Model/Product/Options.php | 2 +- .../Msrp/Model/Quote/Address/CanApplyMsrp.php | 2 +- app/code/Magento/Msrp/Model/Quote/Msrp.php | 2 +- .../Quote/SetCanApplyMsrpObserver.php | 2 +- .../Catalog/Product/Edit/Tab/Attributes.php | 2 +- .../Magento/Msrp/Pricing/Price/MsrpPrice.php | 2 +- .../Msrp/Pricing/Price/MsrpPriceInterface.php | 2 +- app/code/Magento/Msrp/Setup/InstallData.php | 2 +- app/code/Magento/Msrp/Setup/UpgradeData.php | 64 + .../Msrp/Test/Unit/Helper/DataTest.php | 2 +- .../Attribute/Source/Type/PriceTest.php | 2 +- .../Quote/SetCanApplyMsrpObserverTest.php | 2 +- .../Test/Unit/Pricing/Price/MsrpPriceTest.php | 2 +- .../Product/Form/Modifier/MsrpTest.php | 2 +- .../Product/Form/Modifier/Msrp.php | 2 +- app/code/Magento/Msrp/composer.json | 2 +- app/code/Magento/Msrp/etc/adminhtml/di.xml | 2 +- .../Magento/Msrp/etc/adminhtml/system.xml | 2 +- .../Magento/Msrp/etc/catalog_attributes.xml | 2 +- app/code/Magento/Msrp/etc/config.xml | 2 +- app/code/Magento/Msrp/etc/di.xml | 2 +- app/code/Magento/Msrp/etc/frontend/events.xml | 2 +- app/code/Magento/Msrp/etc/module.xml | 4 +- .../Magento/Msrp/etc/webapi_rest/events.xml | 2 +- .../Magento/Msrp/etc/webapi_soap/events.xml | 2 +- app/code/Magento/Msrp/registration.php | 2 +- .../base/layout/catalog_product_prices.xml | 2 +- .../base/templates/product/price/msrp.phtml | 2 +- .../Magento/Msrp/view/base/web/js/msrp.js | 2 +- .../frontend/layout/catalog_category_view.xml | 2 +- .../layout/catalog_product_compare_index.xml | 2 +- .../frontend/layout/catalog_product_view.xml | 2 +- ...catalog_product_view_type_downloadable.xml | 2 +- .../layout/catalogsearch_advanced_result.xml | 2 +- .../layout/catalogsearch_result_index.xml | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../checkout_cart_sidebar_total_renderers.xml | 2 +- .../layout/checkout_onepage_failure.xml | 2 +- .../layout/checkout_onepage_success.xml | 2 +- .../Msrp/view/frontend/layout/msrp_popup.xml | 2 +- .../frontend/layout/review_product_list.xml | 2 +- ...list_index_configure_type_downloadable.xml | 2 +- .../frontend/layout/wishlist_index_index.xml | 2 +- .../frontend/layout/wishlist_search_view.xml | 2 +- .../frontend/layout/wishlist_shared_index.xml | 2 +- .../Msrp/view/frontend/requirejs-config.js | 4 +- .../frontend/templates/cart/subtotal.phtml | 2 +- .../view/frontend/templates/cart/totals.phtml | 2 +- .../Msrp/view/frontend/templates/popup.phtml | 2 +- .../render/item/price_msrp_item.phtml | 2 +- .../render/item/price_msrp_rss.phtml | 2 +- .../view/checkout/minicart/subtotal/totals.js | 2 +- .../checkout/minicart/subtotal/totals.html | 2 +- .../Block/Checkout/AbstractMultishipping.php | 2 +- .../Block/Checkout/Address/Select.php | 2 +- .../Block/Checkout/Addresses.php | 2 +- .../Multishipping/Block/Checkout/Billing.php | 2 +- .../Block/Checkout/Billing/Items.php | 4 +- .../Multishipping/Block/Checkout/Link.php | 2 +- .../Multishipping/Block/Checkout/Overview.php | 4 +- .../Block/Checkout/Payment/Info.php | 2 +- .../Multishipping/Block/Checkout/Shipping.php | 2 +- .../Multishipping/Block/Checkout/State.php | 2 +- .../Multishipping/Block/Checkout/Success.php | 2 +- .../Multishipping/Controller/Checkout.php | 2 +- .../Controller/Checkout/Address.php | 2 +- .../Checkout/Address/EditAddress.php | 2 +- .../Checkout/Address/EditBilling.php | 2 +- .../Checkout/Address/EditShipping.php | 2 +- .../Checkout/Address/EditShippingPost.php | 2 +- .../Checkout/Address/NewBilling.php | 2 +- .../Checkout/Address/NewShipping.php | 2 +- .../Checkout/Address/SaveBilling.php | 2 +- .../Checkout/Address/SelectBilling.php | 2 +- .../Checkout/Address/SetBilling.php | 2 +- .../Checkout/Address/ShippingSaved.php | 2 +- .../Controller/Checkout/Addresses.php | 2 +- .../Controller/Checkout/AddressesPost.php | 2 +- .../Controller/Checkout/BackToAddresses.php | 2 +- .../Controller/Checkout/BackToBilling.php | 2 +- .../Controller/Checkout/BackToShipping.php | 2 +- .../Controller/Checkout/Billing.php | 2 +- .../Controller/Checkout/Index.php | 2 +- .../Controller/Checkout/Login.php | 2 +- .../Controller/Checkout/Overview.php | 2 +- .../Controller/Checkout/OverviewPost.php | 2 +- .../Controller/Checkout/Plugin.php | 2 +- .../Controller/Checkout/Register.php | 2 +- .../Controller/Checkout/RemoveItem.php | 2 +- .../Controller/Checkout/Shipping.php | 2 +- .../Controller/Checkout/ShippingPost.php | 2 +- .../Controller/Checkout/Success.php | 2 +- .../Magento/Multishipping/Helper/Data.php | 2 +- app/code/Magento/Multishipping/Helper/Url.php | 2 +- .../Model/Cart/Controller/CartPlugin.php | 2 +- .../Model/Checkout/Type/Multishipping.php | 4 +- .../Checkout/Type/Multishipping/Plugin.php | 2 +- .../Checkout/Type/Multishipping/State.php | 2 +- .../Payment/Method/Specification/Enabled.php | 2 +- .../Block/Checkout/Address/SelectTest.php | 2 +- .../Unit/Block/Checkout/Billing/ItemsTest.php | 40 + .../Test/Unit/Block/Checkout/OverviewTest.php | 18 +- .../Unit/Block/Checkout/Payment/InfoTest.php | 2 +- .../Test/Unit/Block/Checkout/ShippingTest.php | 2 +- .../Test/Unit/Block/Checkout/StateTest.php | 2 +- .../Test/Unit/Block/Checkout/SuccessTest.php | 2 +- .../Checkout/Address/EditAddressTest.php | 2 +- .../Checkout/Address/EditBillingTest.php | 2 +- .../Checkout/Address/EditShippingTest.php | 2 +- .../Checkout/Address/NewBillingTest.php | 2 +- .../Checkout/Address/NewShippingTest.php | 2 +- .../Checkout/Address/ShippingSavedTest.php | 2 +- .../Unit/Controller/Checkout/PluginTest.php | 2 +- .../Test/Unit/Helper/DataTest.php | 2 +- .../Model/Cart/Controller/CartPluginTest.php | 2 +- .../Type/Multishipping/PluginTest.php | 2 +- .../Model/Checkout/Type/MultishippingTest.php | 353 +- .../Method/Specification/EnabledTest.php | 2 +- app/code/Magento/Multishipping/composer.json | 2 +- app/code/Magento/Multishipping/etc/acl.xml | 2 +- .../Multishipping/etc/adminhtml/system.xml | 2 +- app/code/Magento/Multishipping/etc/config.xml | 2 +- .../Magento/Multishipping/etc/frontend/di.xml | 2 +- .../Multishipping/etc/frontend/page_types.xml | 2 +- .../Multishipping/etc/frontend/routes.xml | 2 +- .../Multishipping/etc/frontend/sections.xml | 2 +- app/code/Magento/Multishipping/etc/module.xml | 2 +- .../Magento/Multishipping/registration.php | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../layout/multishipping_checkout.xml | 2 +- ...ishipping_checkout_address_editaddress.xml | 2 +- ...ishipping_checkout_address_editbilling.xml | 2 +- ...shipping_checkout_address_editshipping.xml | 2 +- ...tishipping_checkout_address_newbilling.xml | 2 +- ...ishipping_checkout_address_newshipping.xml | 2 +- .../multishipping_checkout_address_select.xml | 2 +- ...hipping_checkout_address_selectbilling.xml | 2 +- .../multishipping_checkout_addresses.xml | 2 +- .../layout/multishipping_checkout_billing.xml | 2 +- ...ultishipping_checkout_customer_address.xml | 2 +- .../layout/multishipping_checkout_login.xml | 2 +- .../multishipping_checkout_overview.xml | 2 +- .../multishipping_checkout_register.xml | 2 +- .../multishipping_checkout_shipping.xml | 2 +- .../layout/multishipping_checkout_success.xml | 2 +- .../view/frontend/requirejs-config.js | 4 +- .../templates/checkout/address/select.phtml | 2 +- .../templates/checkout/addresses.phtml | 2 +- .../frontend/templates/checkout/billing.phtml | 2 +- .../templates/checkout/billing/items.phtml | 2 +- .../templates/checkout/item/default.phtml | 2 +- .../frontend/templates/checkout/link.phtml | 2 +- .../templates/checkout/overview.phtml | 2 +- .../templates/checkout/overview/item.phtml | 2 +- .../templates/checkout/shipping.phtml | 2 +- .../frontend/templates/checkout/state.phtml | 2 +- .../frontend/templates/checkout/success.phtml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../multishipping/item/default.phtml | 2 +- .../view/frontend/web/js/multi-shipping.js | 27 +- .../view/frontend/web/js/overview.js | 20 +- .../view/frontend/web/js/payment.js | 11 +- .../Model/Apm/Deployments.php | 2 +- .../NewRelicReporting/Model/Config.php | 2 +- .../NewRelicReporting/Model/Counter.php | 2 +- .../NewRelicReporting/Model/Counts.php | 2 +- .../Magento/NewRelicReporting/Model/Cron.php | 2 +- .../Model/Cron/ReportCounts.php | 2 +- .../Model/Cron/ReportModulesInfo.php | 2 +- .../Model/Cron/ReportNewRelicCron.php | 2 +- .../NewRelicReporting/Model/CronEvent.php | 2 +- .../NewRelicReporting/Model/Module.php | 2 +- .../Model/Module/Collect.php | 2 +- .../Model/NewRelicWrapper.php | 2 +- .../Model/Observer/CheckConfig.php | 2 +- .../Model/Observer/ReportConcurrentAdmins.php | 2 +- .../ReportConcurrentAdminsToNewRelic.php | 2 +- .../Model/Observer/ReportConcurrentUsers.php | 2 +- .../ReportConcurrentUsersToNewRelic.php | 2 +- .../Model/Observer/ReportOrderPlaced.php | 2 +- .../Observer/ReportOrderPlacedToNewRelic.php | 2 +- .../Model/Observer/ReportProductDeleted.php | 2 +- .../ReportProductDeletedToNewRelic.php | 2 +- .../Model/Observer/ReportProductSaved.php | 2 +- .../Observer/ReportProductSavedToNewRelic.php | 2 +- .../Model/Observer/ReportSystemCacheFlush.php | 2 +- .../ReportSystemCacheFlushToNewRelic.php | 2 +- .../NewRelicReporting/Model/Orders.php | 2 +- .../Model/ResourceModel/Counts.php | 2 +- .../Model/ResourceModel/Counts/Collection.php | 2 +- .../Model/ResourceModel/Module.php | 2 +- .../Model/ResourceModel/Module/Collection.php | 2 +- .../Model/ResourceModel/Orders.php | 2 +- .../Model/ResourceModel/Orders/Collection.php | 2 +- .../Model/ResourceModel/System.php | 2 +- .../Model/ResourceModel/System/Collection.php | 2 +- .../Model/ResourceModel/Users.php | 2 +- .../Model/ResourceModel/Users/Collection.php | 2 +- .../NewRelicReporting/Model/System.php | 2 +- .../Magento/NewRelicReporting/Model/Users.php | 2 +- .../NewRelicReporting/Setup/InstallSchema.php | 2 +- .../Test/Unit/Model/Apm/DeploymentsTest.php | 2 +- .../Test/Unit/Model/CounterTest.php | 2 +- .../Test/Unit/Model/Cron/ReportCountsTest.php | 2 +- .../Unit/Model/Cron/ReportModulesInfoTest.php | 2 +- .../Model/Cron/ReportNewRelicCronTest.php | 2 +- .../Test/Unit/Model/CronEventTest.php | 2 +- .../Test/Unit/Model/CronTest.php | 2 +- .../Test/Unit/Model/Module/CollectTest.php | 2 +- .../Unit/Model/Observer/CheckConfigTest.php | 2 +- .../Observer/ReportConcurrentAdminsTest.php | 2 +- .../ReportConcurrentAdminsToNewRelicTest.php | 2 +- .../Observer/ReportConcurrentUsersTest.php | 2 +- .../ReportConcurrentUsersToNewRelicTest.php | 2 +- .../Model/Observer/ReportOrderPlacedTest.php | 2 +- .../ReportOrderPlacedToNewRelicTest.php | 2 +- .../Observer/ReportProductDeletedTest.php | 2 +- .../ReportProductDeletedToNewRelicTest.php | 2 +- .../Model/Observer/ReportProductSavedTest.php | 2 +- .../ReportProductSavedToNewRelicTest.php | 2 +- .../Observer/ReportSystemCacheFlushTest.php | 2 +- .../ReportSystemCacheFlushToNewRelicTest.php | 2 +- .../Magento/NewRelicReporting/composer.json | 2 +- .../Magento/NewRelicReporting/etc/acl.xml | 2 +- .../etc/adminhtml/events.xml | 2 +- .../etc/adminhtml/system.xml | 2 +- .../Magento/NewRelicReporting/etc/config.xml | 2 +- .../Magento/NewRelicReporting/etc/crontab.xml | 2 +- app/code/Magento/NewRelicReporting/etc/di.xml | 12 +- .../Magento/NewRelicReporting/etc/events.xml | 2 +- .../NewRelicReporting/etc/frontend/events.xml | 2 +- .../Magento/NewRelicReporting/etc/module.xml | 2 +- .../NewRelicReporting/registration.php | 2 +- .../Newsletter/Block/Adminhtml/Problem.php | 2 +- .../Problem/Grid/Filter/Checkbox.php | 2 +- .../Problem/Grid/Renderer/Checkbox.php | 2 +- .../Newsletter/Block/Adminhtml/Queue/Edit.php | 2 +- .../Block/Adminhtml/Queue/Edit/Form.php | 2 +- .../Adminhtml/Queue/Grid/Renderer/Action.php | 2 +- .../Block/Adminhtml/Queue/Preview.php | 2 +- .../Block/Adminhtml/Queue/Preview/Form.php | 2 +- .../Newsletter/Block/Adminhtml/Subscriber.php | 2 +- .../Block/Adminhtml/Subscriber/Grid.php | 2 +- .../Subscriber/Grid/Filter/Checkbox.php | 2 +- .../Subscriber/Grid/Filter/Website.php | 2 +- .../Subscriber/Grid/Renderer/Checkbox.php | 2 +- .../Newsletter/Block/Adminhtml/Template.php | 2 +- .../Block/Adminhtml/Template/Edit.php | 2 +- .../Block/Adminhtml/Template/Edit/Form.php | 2 +- .../Block/Adminhtml/Template/Grid.php | 2 +- .../Template/Grid/Renderer/Action.php | 2 +- .../Template/Grid/Renderer/Sender.php | 2 +- .../Block/Adminhtml/Template/Preview.php | 2 +- .../Block/Adminhtml/Template/Preview/Form.php | 2 +- .../Magento/Newsletter/Block/Subscribe.php | 2 +- .../Grid/Options/GroupOptionHash.php | 2 +- .../Grid/Options/StoreOptionHash.php | 2 +- .../Controller/Adminhtml/Problem.php | 2 +- .../Controller/Adminhtml/Problem/Grid.php | 2 +- .../Controller/Adminhtml/Problem/Index.php | 2 +- .../Newsletter/Controller/Adminhtml/Queue.php | 2 +- .../Controller/Adminhtml/Queue/Cancel.php | 2 +- .../Controller/Adminhtml/Queue/Drop.php | 2 +- .../Controller/Adminhtml/Queue/Edit.php | 2 +- .../Controller/Adminhtml/Queue/Grid.php | 2 +- .../Controller/Adminhtml/Queue/Index.php | 2 +- .../Controller/Adminhtml/Queue/Pause.php | 2 +- .../Controller/Adminhtml/Queue/Preview.php | 2 +- .../Controller/Adminhtml/Queue/Resume.php | 2 +- .../Controller/Adminhtml/Queue/Save.php | 2 +- .../Controller/Adminhtml/Queue/Sending.php | 2 +- .../Controller/Adminhtml/Queue/Start.php | 2 +- .../Controller/Adminhtml/Subscriber.php | 2 +- .../Adminhtml/Subscriber/ExportCsv.php | 2 +- .../Adminhtml/Subscriber/ExportXml.php | 2 +- .../Controller/Adminhtml/Subscriber/Grid.php | 2 +- .../Controller/Adminhtml/Subscriber/Index.php | 2 +- .../Adminhtml/Subscriber/MassDelete.php | 2 +- .../Adminhtml/Subscriber/MassUnsubscribe.php | 2 +- .../Controller/Adminhtml/Template.php | 2 +- .../Controller/Adminhtml/Template/Delete.php | 2 +- .../Controller/Adminhtml/Template/Drop.php | 2 +- .../Controller/Adminhtml/Template/Edit.php | 2 +- .../Controller/Adminhtml/Template/Grid.php | 2 +- .../Controller/Adminhtml/Template/Index.php | 2 +- .../Adminhtml/Template/NewAction.php | 2 +- .../Controller/Adminhtml/Template/Preview.php | 2 +- .../Controller/Adminhtml/Template/Save.php | 2 +- .../Magento/Newsletter/Controller/Manage.php | 2 +- .../Newsletter/Controller/Manage/Index.php | 2 +- .../Newsletter/Controller/Manage/Save.php | 2 +- .../Newsletter/Controller/Subscriber.php | 2 +- .../Controller/Subscriber/Confirm.php | 2 +- .../Controller/Subscriber/NewAction.php | 2 +- .../Controller/Subscriber/Unsubscribe.php | 2 +- app/code/Magento/Newsletter/Helper/Data.php | 2 +- .../Magento/Newsletter/Model/Observer.php | 2 +- .../Model/Plugin/CustomerPlugin.php | 2 +- app/code/Magento/Newsletter/Model/Problem.php | 2 +- app/code/Magento/Newsletter/Model/Queue.php | 2 +- .../Newsletter/Model/Queue/Options/Status.php | 2 +- .../Model/Queue/TransportBuilder.php | 2 +- .../Model/ResourceModel/Grid/Collection.php | 2 +- .../Model/ResourceModel/Problem.php | 2 +- .../ResourceModel/Problem/Collection.php | 2 +- .../Newsletter/Model/ResourceModel/Queue.php | 12 +- .../Model/ResourceModel/Queue/Collection.php | 2 +- .../ResourceModel/Queue/Grid/Collection.php | 2 +- .../Model/ResourceModel/Subscriber.php | 2 +- .../ResourceModel/Subscriber/Collection.php | 2 +- .../Subscriber/Grid/Collection.php | 2 +- .../Model/ResourceModel/Template.php | 2 +- .../ResourceModel/Template/Collection.php | 2 +- app/code/Magento/Newsletter/Model/Session.php | 2 +- .../Magento/Newsletter/Model/Subscriber.php | 12 +- .../Magento/Newsletter/Model/Template.php | 10 +- .../Newsletter/Model/Template/Filter.php | 2 +- .../Newsletter/Setup/InstallSchema.php | 2 +- .../Block/Adminhtml/Queue/PreviewTest.php | 2 +- .../Block/Adminhtml/Template/PreviewTest.php | 2 +- .../Test/Unit/Controller/Manage/SaveTest.php | 2 +- .../Unit/Model/Plugin/CustomerPluginTest.php | 2 +- .../Unit/Model/Queue/TransportBuilderTest.php | 2 +- .../Newsletter/Test/Unit/Model/QueueTest.php | 2 +- .../Test/Unit/Model/SubscriberTest.php | 33 +- .../Test/Unit/Model/Template/FilterTest.php | 2 +- .../Test/Unit/Model/TemplateTest.php | 53 +- app/code/Magento/Newsletter/composer.json | 2 +- app/code/Magento/Newsletter/etc/acl.xml | 2 +- .../Magento/Newsletter/etc/adminhtml/menu.xml | 2 +- .../Newsletter/etc/adminhtml/routes.xml | 4 +- .../Newsletter/etc/adminhtml/system.xml | 2 +- app/code/Magento/Newsletter/etc/config.xml | 2 +- app/code/Magento/Newsletter/etc/crontab.xml | 2 +- app/code/Magento/Newsletter/etc/di.xml | 2 +- .../Newsletter/etc/email_templates.xml | 2 +- .../Newsletter/etc/extension_attributes.xml | 4 +- .../Magento/Newsletter/etc/frontend/di.xml | 2 +- .../Newsletter/etc/frontend/page_types.xml | 2 +- .../Newsletter/etc/frontend/routes.xml | 4 +- app/code/Magento/Newsletter/etc/module.xml | 2 +- app/code/Magento/Newsletter/registration.php | 2 +- .../adminhtml/layout/customer_index_edit.xml | 2 +- .../layout/newsletter_problem_block.xml | 2 +- .../layout/newsletter_problem_grid.xml | 2 +- .../layout/newsletter_problem_index.xml | 2 +- .../layout/newsletter_queue_edit.xml | 2 +- .../layout/newsletter_queue_grid.xml | 2 +- .../layout/newsletter_queue_grid_block.xml | 2 +- .../layout/newsletter_queue_index.xml | 2 +- .../layout/newsletter_queue_preview.xml | 2 +- .../layout/newsletter_queue_preview_popup.xml | 2 +- .../layout/newsletter_subscriber_block.xml | 2 +- .../newsletter_subscriber_exportcsv.xml | 2 +- .../newsletter_subscriber_exportxml.xml | 2 +- .../layout/newsletter_subscriber_grid.xml | 2 +- .../layout/newsletter_subscriber_index.xml | 2 +- .../layout/newsletter_template_edit.xml | 2 +- .../layout/newsletter_template_preview.xml | 2 +- .../newsletter_template_preview_popup.xml | 2 +- .../view/adminhtml/layout/preview.xml | 2 +- .../templates/preview/iframeswitcher.phtml | 2 +- .../adminhtml/templates/preview/store.phtml | 2 +- .../adminhtml/templates/problem/list.phtml | 2 +- .../view/adminhtml/templates/queue/edit.phtml | 2 +- .../view/adminhtml/templates/queue/list.phtml | 2 +- .../adminhtml/templates/queue/preview.phtml | 2 +- .../adminhtml/templates/subscriber/list.phtml | 2 +- .../adminhtml/templates/template/edit.phtml | 2 +- .../adminhtml/templates/template/list.phtml | 2 +- .../templates/template/preview.phtml | 2 +- .../view/frontend/email/subscr_confirm.html | 2 +- .../view/frontend/email/subscr_success.html | 2 +- .../view/frontend/email/unsub_success.html | 2 +- .../view/frontend/layout/customer_account.xml | 5 +- .../view/frontend/layout/default.xml | 2 +- .../layout/newsletter_manage_index.xml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../view/frontend/templates/subscribe.phtml | 4 +- .../Block/Form/AbstractInstruction.php | 2 +- .../Block/Form/Banktransfer.php | 2 +- .../Block/Form/Cashondelivery.php | 2 +- .../OfflinePayments/Block/Form/Checkmo.php | 2 +- .../Block/Form/Purchaseorder.php | 2 +- .../OfflinePayments/Block/Info/Checkmo.php | 15 +- .../Block/Info/Purchaseorder.php | 2 +- .../OfflinePayments/Model/Banktransfer.php | 2 +- .../OfflinePayments/Model/Cashondelivery.php | 2 +- .../Magento/OfflinePayments/Model/Checkmo.php | 2 +- .../Model/CheckmoConfigProvider.php | 2 +- .../Model/InstructionsConfigProvider.php | 2 +- .../OfflinePayments/Model/Purchaseorder.php | 2 +- .../BeforeOrderPaymentSaveObserver.php | 2 +- .../Block/Form/AbstractInstructionTest.php | 2 +- .../Test/Unit/Block/Info/CheckmoTest.php | 93 +- .../Test/Unit/Model/BanktransferTest.php | 2 +- .../Test/Unit/Model/CashondeliveryTest.php | 2 +- .../Unit/Model/CheckmoConfigProviderTest.php | 2 +- .../Test/Unit/Model/CheckmoTest.php | 2 +- .../Model/InstructionsConfigProviderTest.php | 2 +- .../Test/Unit/Model/PurchaseorderTest.php | 2 +- .../BeforeOrderPaymentSaveObserverTest.php | 2 +- .../Magento/OfflinePayments/composer.json | 2 +- .../OfflinePayments/etc/adminhtml/system.xml | 2 +- .../Magento/OfflinePayments/etc/config.xml | 2 +- .../Magento/OfflinePayments/etc/events.xml | 2 +- .../OfflinePayments/etc/frontend/di.xml | 2 +- .../Magento/OfflinePayments/etc/module.xml | 2 +- .../Magento/OfflinePayments/etc/payment.xml | 2 +- .../Magento/OfflinePayments/registration.php | 2 +- .../templates/form/banktransfer.phtml | 2 +- .../templates/form/cashondelivery.phtml | 2 +- .../adminhtml/templates/form/checkmo.phtml | 2 +- .../templates/form/purchaseorder.phtml | 2 +- .../adminhtml/templates/info/checkmo.phtml | 4 +- .../templates/info/pdf/checkmo.phtml | 4 +- .../templates/info/pdf/purchaseorder.phtml | 2 +- .../templates/info/purchaseorder.phtml | 2 +- .../frontend/layout/checkout_index_index.xml | 4 +- .../templates/form/banktransfer.phtml | 2 +- .../templates/form/cashondelivery.phtml | 2 +- .../frontend/templates/form/checkmo.phtml | 2 +- .../templates/form/purchaseorder.phtml | 2 +- .../frontend/templates/info/checkmo.phtml | 4 +- .../templates/info/purchaseorder.phtml | 2 +- .../method-renderer/banktransfer-method.js | 43 +- .../method-renderer/cashondelivery-method.js | 41 +- .../payment/method-renderer/checkmo-method.js | 52 +- .../method-renderer/purchaseorder-method.js | 78 +- .../web/js/view/payment/offline-payments.js | 65 +- .../web/template/payment/banktransfer.html | 2 +- .../web/template/payment/cashondelivery.html | 2 +- .../web/template/payment/checkmo.html | 4 +- .../template/payment/purchaseorder-form.html | 4 +- .../Adminhtml/Carrier/Tablerate/Grid.php | 2 +- .../Block/Adminhtml/Form/Field/Export.php | 2 +- .../Block/Adminhtml/Form/Field/Import.php | 2 +- .../System/Config/ExportTablerates.php | 2 +- .../Model/Carrier/Flatrate.php | 2 +- .../Carrier/Flatrate/ItemPriceCalculator.php | 2 +- .../Model/Carrier/Freeshipping.php | 2 +- .../OfflineShipping/Model/Carrier/Pickup.php | 2 +- .../Model/Carrier/Tablerate.php | 54 +- .../Model/Config/Backend/Tablerate.php | 2 +- .../Model/Config/Source/Flatrate.php | 2 +- .../Model/Config/Source/Tablerate.php | 2 +- .../Plugin/Checkout/Block/Cart/Shipping.php | 2 +- .../Model/Quote/Address/FreeShipping.php | 13 +- .../Model/ResourceModel/Carrier/Tablerate.php | 2 +- .../Tablerate/CSV/ColumnNotFoundException.php | 2 +- .../Carrier/Tablerate/CSV/ColumnResolver.php | 2 +- .../Carrier/Tablerate/CSV/RowException.php | 2 +- .../Carrier/Tablerate/CSV/RowParser.php | 2 +- .../Carrier/Tablerate/Collection.php | 2 +- .../Carrier/Tablerate/DataHashGenerator.php | 2 +- .../Carrier/Tablerate/Import.php | 2 +- .../Carrier/Tablerate/LocationDirectory.php | 2 +- .../Carrier/Tablerate/RateQuery.php | 2 +- .../Model/SalesRule/Calculator.php | 2 +- .../OfflineShipping/Model/SalesRule/Rule.php | 2 +- .../Source/SalesRule/FreeShippingOptions.php | 2 +- .../OfflineShipping/Setup/InstallSchema.php | 2 +- .../Adminhtml/Carrier/Tablerate/GridTest.php | 2 +- .../Block/Adminhtml/Form/Field/ExportTest.php | 2 +- .../Block/Adminhtml/Form/Field/ImportTest.php | 2 +- .../Model/Config/Backend/TablerateTest.php | 2 +- .../Unit/Model/Config/Source/FlatrateTest.php | 2 +- .../Model/Config/Source/TablerateTest.php | 2 +- .../Checkout/Block/Cart/ShippingTest.php | 2 +- .../Model/Quote/Address/FreeShippingTest.php | 111 + .../Tablerate/CSV/ColumnResolverTest.php | 2 +- .../Carrier/Tablerate/CSV/RowParserTest.php | 2 +- .../Carrier/Tablerate/ImportTest.php | 2 +- .../Unit/Model/SalesRule/CalculatorTest.php | 2 +- .../Magento/OfflineShipping/composer.json | 2 +- .../OfflineShipping/etc/adminhtml/routes.xml | 2 +- .../OfflineShipping/etc/adminhtml/system.xml | 4 +- .../Magento/OfflineShipping/etc/config.xml | 2 +- app/code/Magento/OfflineShipping/etc/di.xml | 2 +- .../Magento/OfflineShipping/etc/fieldset.xml | 2 +- .../Magento/OfflineShipping/etc/module.xml | 2 +- .../Magento/OfflineShipping/registration.php | 2 +- .../ui_component/sales_rule_form.xml | 2 +- .../salesrulestaging_update_form.xml | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../flatrate.js | 35 +- .../freeshipping.js | 35 +- .../tablerate.js | 53 +- .../shipping-rates-validator/flatrate.js | 61 +- .../shipping-rates-validator/freeshipping.js | 61 +- .../shipping-rates-validator/tablerate.js | 72 +- .../shipping-rates-validation/flatrate.js | 46 +- .../shipping-rates-validation/freeshipping.js | 46 +- .../shipping-rates-validation/tablerate.js | 46 +- .../Magento/PageCache/Block/Javascript.php | 2 +- .../Block/System/Config/Form/Field/Export.php | 2 +- .../Config/Form/Field/Export/Varnish4.php | 2 +- .../Export/{Varnish3.php => Varnish5.php} | 6 +- .../PageCache/ExportVarnishConfig.php | 6 +- .../Magento/PageCache/Controller/Block.php | 2 +- .../PageCache/Controller/Block/Esi.php | 2 +- .../PageCache/Controller/Block/Render.php | 2 +- app/code/Magento/PageCache/Helper/Data.php | 2 +- .../Model/App/CacheIdentifierPlugin.php | 2 +- .../App/FrontController/BuiltinPlugin.php | 2 +- .../App/FrontController/VarnishPlugin.php | 2 +- .../PageCache/Model/App/PageCachePlugin.php | 2 +- .../Model/App/Response/HttpPlugin.php | 2 +- .../Magento/PageCache/Model/Cache/Server.php | 2 +- .../Magento/PageCache/Model/Cache/Type.php | 2 +- app/code/Magento/PageCache/Model/Config.php | 51 +- .../Model/Controller/Result/BuiltinPlugin.php | 2 +- .../Model/Controller/Result/VarnishPlugin.php | 2 +- .../PageCache/Model/DepersonalizeChecker.php | 2 +- .../Model/Layout/DepersonalizePlugin.php | 2 +- .../PageCache/Model/Layout/LayoutPlugin.php | 2 +- .../PageCache/Model/Layout/MergePlugin.php | 55 + .../Model/System/Config/Backend/Ttl.php | 2 +- .../Model/System/Config/Backend/Varnish.php | 2 +- .../System/Config/Source/Application.php | 2 +- .../PageCache/Observer/FlushAllCache.php | 2 +- .../PageCache/Observer/FlushCacheByTags.php | 35 +- .../Observer/FlushFormKeyOnLogout.php | 2 +- .../PageCache/Observer/InvalidateCache.php | 2 +- .../Observer/ProcessLayoutRenderElement.php | 33 +- .../Observer/RegisterFormKeyFromCookie.php | 2 +- .../Unit/App/CacheIdentifierPluginTest.php | 2 +- .../Test/Unit/Block/Controller/StubBlock.php | 2 +- .../Test/Unit/Block/JavascriptTest.php | 2 +- .../PageCache/ExportVarnishConfigTest.php | 2 +- .../Test/Unit/Controller/Block/EsiTest.php | 2 +- .../Test/Unit/Controller/Block/RenderTest.php | 2 +- .../PageCache/Test/Unit/Helper/DataTest.php | 2 +- .../App/FrontController/BuiltinPluginTest.php | 2 +- .../App/FrontController/VarnishPluginTest.php | 2 +- .../Unit/Model/App/PageCachePluginTest.php | 2 +- .../Model/App/Response/HttpPluginTest.php | 2 +- .../Test/Unit/Model/Cache/ServerTest.php | 2 +- .../Test/Unit/Model/Cache/TypeTest.php | 2 +- .../PageCache/Test/Unit/Model/ConfigTest.php | 77 +- .../Controller/Result/BuiltinPluginTest.php | 2 +- .../Controller/Result/VarnishPluginTest.php | 2 +- .../Unit/Model/DepersonalizeCheckerTest.php | 2 +- .../Model/Layout/DepersonalizePluginTest.php | 2 +- .../Unit/Model/Layout/LayoutPluginTest.php | 2 +- .../Test/Unit/Model/_files/result.vcl | 11 +- .../PageCache/Test/Unit/Model/_files/test.vcl | 11 +- .../Test/Unit/Observer/FlushAllCacheTest.php | 2 +- .../Unit/Observer/FlushCacheByTagsTest.php | 15 +- .../Observer/FlushFormKeyOnLogoutTest.php | 2 +- .../Unit/Observer/InvalidateCacheTest.php | 2 +- .../ProcessLayoutRenderElementTest.php | 43 +- .../RegisterFormKeyFromCookieTest.php | 2 +- app/code/Magento/PageCache/composer.json | 2 +- .../Magento/PageCache/etc/adminhtml/di.xml | 2 +- .../PageCache/etc/adminhtml/routes.xml | 2 +- .../PageCache/etc/adminhtml/system.xml | 20 +- app/code/Magento/PageCache/etc/cache.xml | 2 +- app/code/Magento/PageCache/etc/config.xml | 9 +- app/code/Magento/PageCache/etc/di.xml | 2 +- app/code/Magento/PageCache/etc/events.xml | 7 +- .../Magento/PageCache/etc/frontend/di.xml | 5 +- .../Magento/PageCache/etc/frontend/events.xml | 12 + .../Magento/PageCache/etc/frontend/routes.xml | 2 +- app/code/Magento/PageCache/etc/module.xml | 2 +- app/code/Magento/PageCache/etc/varnish3.vcl | 139 - app/code/Magento/PageCache/etc/varnish4.vcl | 57 +- app/code/Magento/PageCache/etc/varnish5.vcl | 218 + app/code/Magento/PageCache/i18n/en_US.csv | 4 +- app/code/Magento/PageCache/registration.php | 2 +- .../layout/adminhtml_system_config_edit.xml | 2 +- .../templates/page_cache_validation.phtml | 2 +- .../view/frontend/layout/default.xml | 2 +- .../view/frontend/requirejs-config.js | 2 +- .../view/frontend/templates/javascript.phtml | 4 +- .../frontend/templates/js/components.phtml | 2 +- .../view/frontend/web/js/page-cache.js | 75 +- .../Api/Data/PaymentMethodInterface.php | 2 +- .../Api/PaymentMethodListInterface.php | 2 +- .../Block/Adminhtml/Transparent/Form.php | 2 +- .../Payment/Block/ConfigurableInfo.php | 2 +- app/code/Magento/Payment/Block/Form.php | 2 +- app/code/Magento/Payment/Block/Form/Cc.php | 2 +- .../Magento/Payment/Block/Form/Container.php | 2 +- app/code/Magento/Payment/Block/Info.php | 2 +- .../Payment/Block/Info/AbstractContainer.php | 2 +- app/code/Magento/Payment/Block/Info/Cc.php | 2 +- .../Payment/Block/Info/Instructions.php | 2 +- .../Payment/Block/Info/Substitution.php | 2 +- .../Payment/Block/Transparent/Form.php | 2 +- .../Payment/Block/Transparent/Iframe.php | 2 +- .../Payment/Block/Transparent/Info.php | 2 +- .../Gateway/Command/CommandException.php | 2 +- .../Gateway/Command/CommandManager.php | 2 +- .../Command/CommandManagerInterface.php | 2 +- .../Gateway/Command/CommandManagerPool.php | 2 +- .../Command/CommandManagerPoolInterface.php | 2 +- .../Payment/Gateway/Command/CommandPool.php | 2 +- .../Gateway/Command/CommandPoolInterface.php | 2 +- .../Gateway/Command/GatewayCommand.php | 2 +- .../Payment/Gateway/Command/NullCommand.php | 2 +- .../Gateway/Command/Result/ArrayResult.php | 2 +- .../Gateway/Command/Result/BoolResult.php | 2 +- .../Gateway/Command/ResultInterface.php | 2 +- .../Payment/Gateway/CommandInterface.php | 2 +- .../Magento/Payment/Gateway/Config/Config.php | 2 +- .../Payment/Gateway/Config/ConfigFactory.php | 2 +- .../Gateway/Config/ConfigValueHandler.php | 2 +- .../Gateway/Config/ValueHandlerInterface.php | 2 +- .../Gateway/Config/ValueHandlerPool.php | 2 +- .../Config/ValueHandlerPoolInterface.php | 2 +- .../Gateway/ConfigFactoryInterface.php | 2 +- .../Payment/Gateway/ConfigInterface.php | 2 +- .../Gateway/Data/AddressAdapterInterface.php | 2 +- .../Gateway/Data/Order/AddressAdapter.php | 2 +- .../Gateway/Data/Order/OrderAdapter.php | 2 +- .../Gateway/Data/OrderAdapterInterface.php | 2 +- .../Gateway/Data/PaymentDataObject.php | 2 +- .../Gateway/Data/PaymentDataObjectFactory.php | 2 +- .../PaymentDataObjectFactoryInterface.php | 2 +- .../Data/PaymentDataObjectInterface.php | 2 +- .../Gateway/Data/Quote/AddressAdapter.php | 2 +- .../Gateway/Data/Quote/QuoteAdapter.php | 2 +- .../Payment/Gateway/Helper/ContextHelper.php | 2 +- .../Payment/Gateway/Helper/SubjectReader.php | 2 +- .../Payment/Gateway/Http/Client/Soap.php | 2 +- .../Payment/Gateway/Http/Client/Zend.php | 2 +- .../Payment/Gateway/Http/ClientException.php | 2 +- .../Payment/Gateway/Http/ClientInterface.php | 2 +- .../Http/Converter/HtmlFormConverter.php | 2 +- .../Converter/Soap/ObjectToArrayConverter.php | 2 +- .../Gateway/Http/ConverterException.php | 2 +- .../Gateway/Http/ConverterInterface.php | 2 +- .../Magento/Payment/Gateway/Http/Transfer.php | 2 +- .../Payment/Gateway/Http/TransferBuilder.php | 2 +- .../Gateway/Http/TransferFactoryInterface.php | 2 +- .../Gateway/Http/TransferInterface.php | 2 +- .../Gateway/Request/BuilderComposite.php | 2 +- .../Gateway/Request/BuilderInterface.php | 2 +- .../Payment/Gateway/Response/HandlerChain.php | 2 +- .../Gateway/Response/HandlerInterface.php | 2 +- .../Gateway/Validator/AbstractValidator.php | 2 +- .../Gateway/Validator/CountryValidator.php | 2 +- .../Payment/Gateway/Validator/Result.php | 2 +- .../Gateway/Validator/ResultInterface.php | 2 +- .../Gateway/Validator/ValidatorComposite.php | 2 +- .../Gateway/Validator/ValidatorInterface.php | 2 +- .../Gateway/Validator/ValidatorPool.php | 2 +- .../Validator/ValidatorPoolInterface.php | 2 +- app/code/Magento/Payment/Helper/Data.php | 2 +- app/code/Magento/Payment/Helper/Formatter.php | 2 +- app/code/Magento/Payment/Model/Cart.php | 2 +- .../Payment/Model/Cart/SalesModel/Factory.php | 2 +- .../Payment/Model/Cart/SalesModel/Order.php | 2 +- .../Payment/Model/Cart/SalesModel/Quote.php | 2 +- .../Cart/SalesModel/SalesModelInterface.php | 2 +- app/code/Magento/Payment/Model/CcConfig.php | 2 +- .../Payment/Model/CcConfigProvider.php | 2 +- .../Payment/Model/CcGenericConfigProvider.php | 2 +- .../Payment/Model/Checks/CanUseCheckout.php | 2 +- .../Payment/Model/Checks/CanUseForCountry.php | 2 +- .../CanUseForCountry/CountryProvider.php | 6 +- .../Model/Checks/CanUseForCurrency.php | 2 +- .../Payment/Model/Checks/CanUseInternal.php | 2 +- .../Payment/Model/Checks/Composite.php | 2 +- .../Model/Checks/SpecificationFactory.php | 2 +- .../Model/Checks/SpecificationInterface.php | 2 +- .../Payment/Model/Checks/TotalMinMax.php | 2 +- .../Payment/Model/Checks/ZeroTotal.php | 2 +- app/code/Magento/Payment/Model/Config.php | 2 +- .../Payment/Model/Config/Converter.php | 2 +- .../Magento/Payment/Model/Config/Reader.php | 2 +- .../Payment/Model/Config/SchemaLocator.php | 2 +- .../Model/Config/Source/Allmethods.php | 2 +- .../Config/Source/Allspecificcountries.php | 2 +- .../Payment/Model/Config/Source/Cctype.php | 2 +- .../Payment/Model/IframeConfigProvider.php | 2 +- app/code/Magento/Payment/Model/Info.php | 2 +- .../Magento/Payment/Model/InfoInterface.php | 2 +- .../Payment/Model/Method/AbstractMethod.php | 2 +- .../Magento/Payment/Model/Method/Adapter.php | 24 +- app/code/Magento/Payment/Model/Method/Cc.php | 2 +- .../Payment/Model/Method/ConfigInterface.php | 2 +- .../Magento/Payment/Model/Method/Factory.php | 2 +- .../Magento/Payment/Model/Method/Free.php | 2 +- .../Payment/Model/Method/InstanceFactory.php | 2 +- .../Magento/Payment/Model/Method/Logger.php | 2 +- .../Model/Method/Online/GatewayInterface.php | 2 +- .../Specification/AbstractSpecification.php | 2 +- .../Model/Method/Specification/Composite.php | 2 +- .../Model/Method/Specification/Factory.php | 2 +- .../Model/Method/SpecificationInterface.php | 2 +- .../Payment/Model/Method/Substitution.php | 2 +- .../Model/Method/TransparentInterface.php | 2 +- .../Magento/Payment/Model/MethodInterface.php | 2 +- app/code/Magento/Payment/Model/MethodList.php | 2 +- .../Magento/Payment/Model/Paygate/Result.php | 2 +- .../Magento/Payment/Model/PaymentMethod.php | 2 +- .../Payment/Model/PaymentMethodList.php | 2 +- .../Model/ResourceModel/Grid/GroupList.php | 2 +- .../Model/ResourceModel/Grid/TypeList.php | 2 +- .../Magento/Payment/Model/Source/Cctype.php | 2 +- .../Magento/Payment/Model/Source/Invoice.php | 2 +- .../Observer/AbstractDataAssignObserver.php | 2 +- .../Observer/SalesOrderBeforeSaveObserver.php | 2 +- ...teOrderStatusForPaymentMethodsObserver.php | 2 +- .../Plugin/PaymentConfigurationProcess.php | 73 + .../Block/Adminhtml/Transparent/FormTest.php | 2 +- .../Adminhtml/Transparent/FormTesting.php | 2 +- .../Test/Unit/Block/Form/ContainerTest.php | 2 +- .../Payment/Test/Unit/Block/FormTest.php | 2 +- .../Payment/Test/Unit/Block/Info/CcTest.php | 2 +- .../Unit/Block/Info/ContainerAbstractTest.php | 2 +- .../Test/Unit/Block/Info/InstructionsTest.php | 2 +- .../Test/Unit/Block/Info/SubstitutionTest.php | 167 +- .../Payment/Test/Unit/Block/InfoTest.php | 2 +- .../Test/Unit/Block/Transparent/FormTest.php | 2 +- .../Unit/Block/Transparent/FormTesting.php | 2 +- .../Unit/Gateway/Command/CommandPoolTest.php | 2 +- .../Gateway/Command/GatewayCommandTest.php | 2 +- .../Test/Unit/Gateway/Config/ConfigTest.php | 2 +- .../Gateway/Config/ConfigValueHandlerTest.php | 2 +- .../Gateway/Config/ValueHandlerPoolTest.php | 2 +- .../Gateway/Data/Order/AddressAdapterTest.php | 2 +- .../Gateway/Data/Order/OrderAdapterTest.php | 2 +- .../Data/PaymentDataObjectFactoryTest.php | 2 +- .../Gateway/Data/PaymentDataObjectTest.php | 2 +- .../Gateway/Data/Quote/AddressAdapterTest.php | 2 +- .../Gateway/Data/Quote/QuoteAdapterTest.php | 2 +- .../Unit/Gateway/Http/Client/SoapTest.php | 2 +- .../Unit/Gateway/Http/Client/ZendTest.php | 2 +- .../Http/Converter/HtmlFormConverterTest.php | 2 +- .../Soap/ObjectToArrayConverterTest.php | 2 +- .../Test/Unit/Gateway/Http/TransferTest.php | 2 +- .../Gateway/Request/BuilderCompositeTest.php | 2 +- .../Gateway/Response/HandlerChainTest.php | 2 +- .../Validator/CountryValidatorTest.php | 2 +- .../Unit/Gateway/Validator/ResultTest.php | 2 +- .../Validator/ValidatorCompositeTest.php | 2 +- .../Gateway/Validator/ValidatorPoolTest.php | 2 +- .../Payment/Test/Unit/Helper/DataTest.php | 2 +- .../Model/Cart/SalesModel/FactoryTest.php | 2 +- .../Unit/Model/Cart/SalesModel/OrderTest.php | 2 +- .../Unit/Model/Cart/SalesModel/QuoteTest.php | 2 +- .../Payment/Test/Unit/Model/CartTest.php | 2 +- .../Test/Unit/Model/CcConfigProviderTest.php | 2 +- .../Payment/Test/Unit/Model/CcConfigTest.php | 2 +- .../Model/CcGenericConfigProviderTest.php | 2 +- .../Unit/Model/Checks/CanUseCheckoutTest.php | 2 +- .../CanUseForCountry/CountryProviderTest.php | 132 +- .../Model/Checks/CanUseForCountryTest.php | 2 +- .../Model/Checks/CanUseForCurrencyTest.php | 2 +- .../Unit/Model/Checks/CanUseInternalTest.php | 2 +- .../Test/Unit/Model/Checks/CompositeTest.php | 2 +- .../Model/Checks/SpecificationFactoryTest.php | 2 +- .../Unit/Model/Checks/TotalMinMaxTest.php | 2 +- .../Test/Unit/Model/Checks/ZeroTotalTest.php | 2 +- .../Test/Unit/Model/Config/ConverterTest.php | 2 +- .../Unit/Model/Config/SchemaLocatorTest.php | 2 +- .../Model/Config/Source/AllmethodsTest.php | 2 +- .../Source/AllspecificcountriesTest.php | 2 +- .../Unit/Model/Config/Source/CctypeTest.php | 2 +- .../Test/Unit/Model/Config/_files/payment.xml | 2 +- .../Payment/Test/Unit/Model/ConfigTest.php | 2 +- .../Payment/Test/Unit/Model/InfoTest.php | 2 +- .../Unit/Model/Method/AbstractMethod/Stub.php | 2 +- .../Unit/Model/Method/AbstractMethodTest.php | 2 +- .../Test/Unit/Model/Method/AdapterTest.php | 35 +- .../Payment/Test/Unit/Model/Method/CcTest.php | 2 +- .../Test/Unit/Model/Method/FactoryTest.php | 2 +- .../Test/Unit/Model/Method/FreeTest.php | 2 +- .../Test/Unit/Model/Method/LoggerTest.php | 2 +- .../Method/Specification/CompositeTest.php | 2 +- .../Method/Specification/FactoryTest.php | 2 +- .../Unit/Model/Method/SubstitutionTest.php | 2 +- .../Test/Unit/Model/MethodListTest.php | 2 +- .../Test/Unit/Model/PaymentMethodListTest.php | 2 +- .../ResourceModel/Grid/GroupListTest.php | 2 +- .../Model/ResourceModel/Grid/TypeListTest.php | 2 +- .../Test/Unit/Model/Source/CctypeTest.php | 2 +- .../Test/Unit/Model/Source/InvoiceTest.php | 2 +- .../SalesOrderBeforeSaveObserverTest.php | 2 +- ...derStatusForPaymentMethodsObserverTest.php | 2 +- .../PaymentConfigurationProcessTest.php | 146 + .../Listing/Column/Method/Options.php | 2 +- app/code/Magento/Payment/composer.json | 2 +- app/code/Magento/Payment/etc/acl.xml | 2 +- .../Magento/Payment/etc/adminhtml/system.xml | 2 +- app/code/Magento/Payment/etc/config.xml | 2 +- app/code/Magento/Payment/etc/di.xml | 2 +- app/code/Magento/Payment/etc/events.xml | 2 +- app/code/Magento/Payment/etc/frontend/di.xml | 7 +- app/code/Magento/Payment/etc/module.xml | 2 +- app/code/Magento/Payment/etc/payment.xml | 2 +- app/code/Magento/Payment/etc/payment.xsd | 2 +- app/code/Magento/Payment/etc/payment_file.xsd | 2 +- app/code/Magento/Payment/registration.php | 2 +- .../view/adminhtml/templates/form/cc.phtml | 2 +- .../adminhtml/templates/info/default.phtml | 2 +- .../templates/info/instructions.phtml | 2 +- .../templates/info/pdf/default.phtml | 2 +- .../templates/info/substitution.phtml | 2 +- .../templates/transparent/form.phtml | 2 +- .../templates/transparent/iframe.phtml | 2 +- .../templates/transparent/info.phtml | 2 +- .../Payment/view/adminhtml/web/transparent.js | 84 +- .../credit-card-data.js | 16 + .../credit-card-number-validator.js | 74 + .../credit-card-type.js | 143 + .../luhn10-validator.js | 20 + .../credit-card-validation/cvv-validator.js | 50 + .../expiration-date-validator.js | 54 + .../expiration-month-validator.js | 42 + .../expiration-year-validator.js | 44 + .../expiration-date-validator/parse-date.js | 29 + .../model/credit-card-validation/validator.js | 68 +- .../frontend/layout/checkout_index_index.xml | 4 +- .../layout/checkout_onepage_review.xml | 2 +- .../Payment/view/frontend/requirejs-config.js | 4 +- .../view/frontend/templates/form/cc.phtml | 2 +- .../frontend/templates/info/default.phtml | 2 +- .../templates/info/instructions.phtml | 2 +- .../frontend/templates/transparent/form.phtml | 2 +- .../templates/transparent/iframe.phtml | 2 +- .../frontend/templates/transparent/info.phtml | 2 +- .../Payment/view/frontend/web/cc-type.js | 18 +- .../credit-card-data.js | 19 - .../credit-card-number-validator.js | 72 - .../credit-card-type.js | 140 - .../luhn10-validator.js | 22 - .../credit-card-validation/cvv-validator.js | 41 - .../expiration-date-validator.js | 51 - .../expiration-month-validator.js | 41 - .../expiration-year-validator.js | 42 - .../expiration-date-validator/parse-date.js | 32 - .../frontend/web/js/view/payment/cc-form.js | 603 +- .../frontend/web/js/view/payment/iframe.js | 404 +- .../payment/method-renderer/free-method.js | 37 +- .../frontend/web/js/view/payment/payments.js | 41 +- .../web/template/payment/cc-form.html | 2 +- .../frontend/web/template/payment/free.html | 2 +- .../frontend/web/template/payment/iframe.html | 2 +- .../Payment/view/frontend/web/transparent.js | 2 +- .../Block/Adminhtml/Billing/Agreement.php | 2 +- .../Adminhtml/Billing/Agreement/Grid.php | 2 +- .../Adminhtml/Billing/Agreement/View.php | 2 +- .../Adminhtml/Billing/Agreement/View/Form.php | 2 +- .../Billing/Agreement/View/Tab/Info.php | 2 +- .../Billing/Agreement/View/Tab/Orders.php | 2 +- .../Adminhtml/Billing/Agreement/View/Tabs.php | 2 +- .../Adminhtml/Customer/Edit/Tab/Agreement.php | 2 +- .../Block/Adminhtml/Payflowpro/CcForm.php | 2 +- .../Block/Adminhtml/Settlement/Details.php | 2 +- .../Adminhtml/Settlement/Details/Form.php | 2 +- .../Block/Adminhtml/Settlement/Report.php | 2 +- .../Block/Adminhtml/Store/SwitcherPlugin.php | 2 +- .../Adminhtml/System/Config/ApiWizard.php | 2 +- .../Adminhtml/System/Config/BmlApiWizard.php | 2 +- .../Adminhtml/System/Config/Field/Country.php | 2 +- .../Config/Field/Depends/BmlApiSortOrder.php | 2 +- .../Config/Field/Depends/BmlSortOrder.php | 2 +- .../Config/Field/Depends/MerchantId.php | 2 +- .../Config/Field/Enable/AbstractEnable.php | 2 +- .../System/Config/Field/Enable/Bml.php | 2 +- .../System/Config/Field/Enable/BmlApi.php | 2 +- .../System/Config/Field/Enable/Express.php | 2 +- .../System/Config/Field/Enable/InContext.php | 2 +- .../Config/Field/Enable/InContextApi.php | 2 +- .../System/Config/Field/Enable/Payment.php | 2 +- .../Adminhtml/System/Config/Field/Hidden.php | 2 +- .../System/Config/Fieldset/Expanded.php | 2 +- .../System/Config/Fieldset/Group.php | 2 +- .../Adminhtml/System/Config/Fieldset/Hint.php | 2 +- .../System/Config/Fieldset/Payment.php | 2 +- .../System/Config/Payflowlink/Advanced.php | 2 +- .../System/Config/Payflowlink/Info.php | 2 +- .../System/Config/ResolutionRules.php | 2 +- .../Paypal/Block/Billing/Agreement/View.php | 2 +- .../Paypal/Block/Billing/Agreements.php | 2 +- app/code/Magento/Paypal/Block/Bml/Banners.php | 2 +- app/code/Magento/Paypal/Block/Bml/Form.php | 2 +- .../Magento/Paypal/Block/Bml/Shortcut.php | 2 +- .../Paypal/Block/Cart/ValidationMessages.php | 2 +- .../Onepage/Success/BillingAgreement.php | 2 +- .../Magento/Paypal/Block/Express/Form.php | 2 +- .../Block/Express/InContext/Component.php | 2 +- .../Express/InContext/Minicart/Button.php | 2 +- .../Magento/Paypal/Block/Express/Review.php | 2 +- .../Paypal/Block/Express/Review/Billing.php | 2 +- .../Paypal/Block/Express/Review/Details.php | 2 +- .../Paypal/Block/Express/Review/Shipping.php | 2 +- .../Magento/Paypal/Block/Express/Shortcut.php | 2 +- .../Magento/Paypal/Block/Hosted/Pro/Form.php | 2 +- .../Paypal/Block/Hosted/Pro/Iframe.php | 2 +- .../Magento/Paypal/Block/Hosted/Pro/Info.php | 2 +- app/code/Magento/Paypal/Block/Iframe.php | 2 +- app/code/Magento/Paypal/Block/Logo.php | 2 +- .../Paypal/Block/Payflow/Advanced/Form.php | 2 +- .../Paypal/Block/Payflow/Advanced/Iframe.php | 2 +- .../Paypal/Block/Payflow/Advanced/Info.php | 2 +- .../Magento/Paypal/Block/Payflow/Bml/Form.php | 2 +- .../Block/Payflow/Customer/CardRenderer.php | 2 +- .../Magento/Paypal/Block/Payflow/Info.php | 2 +- .../Paypal/Block/Payflow/Link/Form.php | 2 +- .../Paypal/Block/Payflow/Link/Iframe.php | 2 +- .../Paypal/Block/Payflow/Link/Info.php | 2 +- .../Paypal/Block/PayflowExpress/Form.php | 2 +- .../Block/Payment/Form/Billing/Agreement.php | 2 +- .../Magento/Paypal/Block/Payment/Info.php | 2 +- .../Block/Payment/Info/Billing/Agreement.php | 2 +- .../Adminhtml/Billing/Agreement.php | 2 +- .../Adminhtml/Billing/Agreement/Cancel.php | 2 +- .../Billing/Agreement/CustomerGrid.php | 2 +- .../Adminhtml/Billing/Agreement/Delete.php | 2 +- .../Adminhtml/Billing/Agreement/Grid.php | 2 +- .../Adminhtml/Billing/Agreement/Index.php | 2 +- .../Billing/Agreement/OrdersGrid.php | 2 +- .../Adminhtml/Billing/Agreement/View.php | 2 +- .../Controller/Adminhtml/Paypal/Reports.php | 2 +- .../Adminhtml/Paypal/Reports/Details.php | 2 +- .../Adminhtml/Paypal/Reports/Fetch.php | 2 +- .../Adminhtml/Paypal/Reports/Grid.php | 2 +- .../Adminhtml/Paypal/Reports/Index.php | 2 +- .../Transparent/RequestSecureToken.php | 2 +- .../Adminhtml/Transparent/Response.php | 2 +- .../Paypal/Controller/Billing/Agreement.php | 2 +- .../Controller/Billing/Agreement/Cancel.php | 2 +- .../Billing/Agreement/CancelWizard.php | 2 +- .../Controller/Billing/Agreement/Index.php | 2 +- .../Billing/Agreement/ReturnWizard.php | 2 +- .../Billing/Agreement/StartWizard.php | 2 +- .../Controller/Billing/Agreement/View.php | 2 +- .../Magento/Paypal/Controller/Bml/Start.php | 2 +- .../Controller/Express/AbstractExpress.php | 2 +- .../Express/AbstractExpress/Cancel.php | 2 +- .../Express/AbstractExpress/Edit.php | 2 +- .../Express/AbstractExpress/PlaceOrder.php | 2 +- .../Express/AbstractExpress/ReturnAction.php | 2 +- .../Express/AbstractExpress/Review.php | 2 +- .../AbstractExpress/SaveShippingMethod.php | 2 +- .../ShippingOptionsCallback.php | 2 +- .../Express/AbstractExpress/Start.php | 2 +- .../AbstractExpress/UpdateShippingMethods.php | 2 +- .../Paypal/Controller/Express/Cancel.php | 2 +- .../Paypal/Controller/Express/Edit.php | 2 +- .../Paypal/Controller/Express/GetToken.php | 2 +- .../Paypal/Controller/Express/PlaceOrder.php | 2 +- .../Controller/Express/ReturnAction.php | 2 +- .../Paypal/Controller/Express/Review.php | 2 +- .../Controller/Express/SaveShippingMethod.php | 2 +- .../Express/ShippingOptionsCallback.php | 2 +- .../Paypal/Controller/Express/Start.php | 2 +- .../Express/UpdateShippingMethods.php | 2 +- .../Paypal/Controller/Hostedpro/Cancel.php | 2 +- .../Paypal/Controller/Hostedpro/Redirect.php | 2 +- .../Controller/Hostedpro/ReturnAction.php | 2 +- .../Magento/Paypal/Controller/Ipn/Index.php | 2 +- .../Magento/Paypal/Controller/Payflow.php | 2 +- .../Controller/Payflow/CancelPayment.php | 2 +- .../Paypal/Controller/Payflow/Form.php | 2 +- .../Paypal/Controller/Payflow/ReturnUrl.php | 2 +- .../Paypal/Controller/Payflow/SilentPost.php | 2 +- .../Payflowadvanced/CancelPayment.php | 2 +- .../Controller/Payflowadvanced/Form.php | 2 +- .../Controller/Payflowadvanced/ReturnUrl.php | 2 +- .../Controller/Payflowadvanced/SilentPost.php | 2 +- .../Paypal/Controller/Payflowbml/Start.php | 2 +- .../Controller/Payflowexpress/Cancel.php | 2 +- .../Paypal/Controller/Payflowexpress/Edit.php | 2 +- .../Controller/Payflowexpress/PlaceOrder.php | 2 +- .../Payflowexpress/ReturnAction.php | 2 +- .../Controller/Payflowexpress/Review.php | 2 +- .../Payflowexpress/SaveShippingMethod.php | 2 +- .../ShippingOptionsCallback.php | 2 +- .../Controller/Payflowexpress/Start.php | 2 +- .../Payflowexpress/UpdateShippingMethods.php | 2 +- .../Transparent/RequestSecureToken.php | 2 +- .../Controller/Transparent/Response.php | 2 +- app/code/Magento/Paypal/Cron/FetchReports.php | 2 +- .../Paypal/CustomerData/BillingAgreement.php | 2 +- .../Command/AuthorizationCommand.php | 2 +- .../Payflowpro/Command/SaleCommand.php | 2 +- app/code/Magento/Paypal/Helper/Backend.php | 2 +- app/code/Magento/Paypal/Helper/Checkout.php | 2 +- app/code/Magento/Paypal/Helper/Data.php | 6 +- app/code/Magento/Paypal/Helper/Hss.php | 2 +- .../Helper/Shortcut/CheckoutValidator.php | 2 +- .../Paypal/Helper/Shortcut/Factory.php | 2 +- .../Paypal/Helper/Shortcut/Validator.php | 2 +- .../Helper/Shortcut/ValidatorInterface.php | 2 +- .../Magento/Paypal/Model/AbstractConfig.php | 2 +- app/code/Magento/Paypal/Model/AbstractIpn.php | 2 +- .../Magento/Paypal/Model/Api/AbstractApi.php | 2 +- app/code/Magento/Paypal/Model/Api/Nvp.php | 2 +- .../Magento/Paypal/Model/Api/PayflowNvp.php | 2 +- .../Paypal/Model/Api/ProcessableException.php | 2 +- .../Magento/Paypal/Model/Api/Type/Factory.php | 2 +- .../Model/Billing/AbstractAgreement.php | 2 +- .../Paypal/Model/Billing/Agreement.php | 2 +- .../Billing/Agreement/MethodInterface.php | 2 +- .../Model/Billing/Agreement/OrdersUpdater.php | 2 +- .../Model/BillingAgreementConfigProvider.php | 2 +- app/code/Magento/Paypal/Model/Bml.php | 2 +- app/code/Magento/Paypal/Model/Cart.php | 2 +- app/code/Magento/Paypal/Model/Cert.php | 2 +- app/code/Magento/Paypal/Model/Config.php | 4 +- .../Magento/Paypal/Model/Config/Factory.php | 2 +- .../Paypal/Model/Config/Rules/Converter.php | 2 +- .../Model/Config/Rules/FileResolver.php | 2 +- .../Paypal/Model/Config/Rules/Reader.php | 2 +- .../Model/Config/Rules/SchemaLocator.php | 2 +- .../Config/Structure/Element/FieldPlugin.php | 2 +- .../Structure/PaymentSectionModifier.php | 113 + .../Paypal/Model/Config/StructurePlugin.php | 80 +- app/code/Magento/Paypal/Model/Direct.php | 2 +- app/code/Magento/Paypal/Model/Express.php | 2 +- .../Magento/Paypal/Model/Express/Checkout.php | 89 +- .../Paypal/Model/Express/Checkout/Factory.php | 2 +- .../Paypal/Model/ExpressConfigProvider.php | 2 +- app/code/Magento/Paypal/Model/Hostedpro.php | 2 +- .../Paypal/Model/Hostedpro/Request.php | 2 +- .../Paypal/Model/IframeConfigProvider.php | 2 +- app/code/Magento/Paypal/Model/Info.php | 2 +- app/code/Magento/Paypal/Model/Ipn.php | 2 +- app/code/Magento/Paypal/Model/IpnFactory.php | 2 +- .../Magento/Paypal/Model/IpnInterface.php | 2 +- .../Magento/Paypal/Model/Method/Agreement.php | 2 +- .../Method/Checks/SpecificationPlugin.php | 2 +- app/code/Magento/Paypal/Model/Payflow/Bml.php | 2 +- app/code/Magento/Paypal/Model/Payflow/Pro.php | 2 +- .../Magento/Paypal/Model/Payflow/Request.php | 2 +- .../Paypal/Model/Payflow/Service/Gateway.php | 2 +- .../Payflow/Service/Request/SecureToken.php | 2 +- .../Handler/CreditCardValidationHandler.php | 2 +- .../Service/Response/Handler/FraudHandler.php | 2 +- .../Response/Handler/HandlerComposite.php | 2 +- .../Response/Handler/HandlerInterface.php | 2 +- .../Payflow/Service/Response/Transaction.php | 2 +- .../Response/Validator/AVSResponse.php | 2 +- .../Service/Response/Validator/CVV2Match.php | 2 +- .../Response/Validator/ResponseValidator.php | 2 +- .../Response/Validator/SecureToken.php | 2 +- .../Service/Response/ValidatorInterface.php | 2 +- .../Paypal/Model/Payflow/Transparent.php | 3 +- .../Ui/Adminhtml/TokenUiComponentProvider.php | 2 +- .../Payflow/Ui/TokenUiComponentProvider.php | 2 +- .../Magento/Paypal/Model/PayflowConfig.php | 2 +- .../Magento/Paypal/Model/PayflowExpress.php | 2 +- .../Paypal/Model/PayflowExpress/Checkout.php | 2 +- .../Magento/Paypal/Model/Payflowadvanced.php | 2 +- app/code/Magento/Paypal/Model/Payflowlink.php | 2 +- app/code/Magento/Paypal/Model/Payflowpro.php | 2 +- .../Method/Billing/AbstractAgreement.php | 2 +- app/code/Magento/Paypal/Model/Pro.php | 2 +- .../Paypal/Model/Report/Settlement.php | 2 +- .../Paypal/Model/Report/Settlement/Row.php | 2 +- .../Model/ResourceModel/Billing/Agreement.php | 2 +- .../Billing/Agreement/Collection.php | 2 +- .../Paypal/Model/ResourceModel/Cert.php | 2 +- .../Model/ResourceModel/Report/Settlement.php | 2 +- .../Settlement/Options/TransactionEvents.php | 2 +- .../ResourceModel/Report/Settlement/Row.php | 2 +- .../Report/Settlement/Row/Collection.php | 2 +- .../Model/System/Config/Backend/Cert.php | 2 +- .../Model/System/Config/Backend/Cron.php | 2 +- .../System/Config/Backend/MerchantCountry.php | 2 +- .../System/Config/Source/BmlPosition.php | 2 +- .../Model/System/Config/Source/BmlSize.php | 2 +- .../System/Config/Source/BuyerCountry.php | 2 +- .../System/Config/Source/FetchingSchedule.php | 2 +- .../Model/System/Config/Source/Logo.php | 2 +- .../System/Config/Source/MerchantCountry.php | 2 +- .../System/Config/Source/PaymentActions.php | 2 +- .../Config/Source/PaymentActions/Express.php | 2 +- .../Config/Source/RequireBillingAddress.php | 2 +- .../Model/System/Config/Source/UrlMethod.php | 2 +- .../System/Config/Source/Yesnoshortcut.php | 2 +- .../AddBillingAgreementToSessionObserver.php | 2 +- .../Observer/AddPaypalShortcutsObserver.php | 2 +- .../Observer/HtmlTransactionIdObserver.php | 2 +- .../Paypal/Observer/PayflowProAddCcData.php | 2 +- ...rictAdminBillingAgreementUsageObserver.php | 2 +- .../Observer/SaveOrderAfterSubmitObserver.php | 2 +- .../SetResponseAfterSaveOrderObserver.php | 2 +- app/code/Magento/Paypal/Setup/InstallData.php | 2 +- .../Magento/Paypal/Setup/InstallSchema.php | 2 +- .../Adminhtml/Store/SwitcherPluginTest.php | 2 +- .../System/Config/Field/CountryTest.php | 2 +- .../Field/Enable/AbstractEnable/Stub.php | 2 +- .../Field/Enable/AbstractEnableTest.php | 2 +- .../System/Config/Fieldset/GroupTest.php | 2 +- .../System/Config/Fieldset/HintTest.php | 2 +- .../System/Config/Fieldset/PaymentTest.php | 2 +- .../System/Config/ResolutionRulesTest.php | 2 +- .../Unit/Block/Billing/Agreement/ViewTest.php | 2 +- .../Unit/Block/Billing/AgreementsTest.php | 16 +- .../Test/Unit/Block/Bml/ShortcutTest.php | 2 +- .../Test/Unit/Block/Express/FormTest.php | 2 +- .../Test/Unit/Block/Express/ReviewTest.php | 16 +- .../Test/Unit/Block/Express/ShortcutTest.php | 2 +- .../Unit/Block/Payflow/Link/IframeTest.php | 2 +- .../Unit/Block/PayflowExpress/FormTest.php | 2 +- .../Billing/Agreement/CancelTest.php | 2 +- .../Controller/Express/PlaceOrderTest.php | 2 +- .../Controller/Express/ReturnActionTest.php | 2 +- .../Unit/Controller/Express/StartTest.php | 2 +- .../Test/Unit/Controller/ExpressTest.php | 2 +- .../Test/Unit/Controller/Ipn/IndexTest.php | 2 +- .../Unit/Controller/Payflow/ReturnUrlTest.php | 2 +- .../Transparent/RequestSecureTokenTest.php | 2 +- .../Controller/Transparent/ResponseTest.php | 2 +- .../Test/Unit/Cron/FetchReportsTest.php | 2 +- .../CustomerData/BillingAgreementTest.php | 2 +- .../Paypal/Test/Unit/Helper/BackendTest.php | 2 +- .../Paypal/Test/Unit/Helper/CheckoutTest.php | 2 +- .../Paypal/Test/Unit/Helper/DataTest.php | 82 +- .../Helper/Shortcut/CheckoutValidatorTest.php | 2 +- .../Test/Unit/Helper/Shortcut/FactoryTest.php | 2 +- .../Unit/Helper/Shortcut/ValidatorTest.php | 2 +- .../Test/Unit/Model/AbstractConfigTest.php | 2 +- .../Test/Unit/Model/AbstractConfigTesting.php | 2 +- .../Paypal/Test/Unit/Model/Api/NvpTest.php | 2 +- .../Model/Api/ProcessableExceptionTest.php | 2 +- .../Model/Billing/AbstractAgreementTest.php | 2 +- .../Billing/Agreement/OrdersUpdaterTest.php | 2 +- .../Test/Unit/Model/Billing/AgreementTest.php | 2 +- .../BillingAgreementConfigProviderTest.php | 2 +- .../Paypal/Test/Unit/Model/CartTest.php | 2 +- .../Unit/Model/Config/Rules/ConverterTest.php | 2 +- .../Config/Rules/ConvertibleContent/rules.xml | 2 +- .../Model/Config/Rules/FileResolverTest.php | 2 +- .../Unit/Model/Config/Rules/ReaderTest.php | 2 +- .../Model/Config/Rules/SchemaLocatorTest.php | 2 +- .../Structure/Element/FieldPluginTest.php | 2 +- .../Structure/PaymentSectionModifierTest.php | 182 + .../payment_section_structure_variations.php | 129 + .../Unit/Model/Config/StructurePluginTest.php | 7 +- .../Paypal/Test/Unit/Model/ConfigTest.php | 2 +- .../Test/Unit/Model/Express/CheckoutTest.php | 2 +- .../Unit/Model/ExpressConfigProviderTest.php | 2 +- .../Paypal/Test/Unit/Model/ExpressTest.php | 2 +- .../Test/Unit/Model/Hostedpro/RequestTest.php | 2 +- .../Unit/Model/IframeConfigProviderTest.php | 2 +- .../Paypal/Test/Unit/Model/InfoTest.php | 2 +- .../Paypal/Test/Unit/Model/IpnTest.php | 2 +- .../Test/Unit/Model/Method/AgreementTest.php | 2 +- .../Method/Checks/SpecificationPluginTest.php | 2 +- .../Model/Payflow/Service/GatewayTest.php | 2 +- .../Service/Request/SecureTokenTest.php | 2 +- .../CreditCardValidationHandlerTest.php | 2 +- .../Response/Handler/FraudHandlerTest.php | 2 +- .../Response/Handler/HandlerCompositeTest.php | 2 +- .../Handler/_files/fps_prexmldata.xml | 4 +- .../Handler/_files/xxe_fps_prexmldata.xml | 4 +- .../Service/Response/TransactionTest.php | 2 +- .../Response/Validator/AVSResponseTest.php | 2 +- .../Response/Validator/CVV2MatchTest.php | 2 +- .../Validator/ResponseValidatorTest.php | 2 +- .../Response/Validator/SecureTokenTest.php | 2 +- .../Unit/Model/Payflow/TransparentTest.php | 9 +- .../Test/Unit/Model/PayflowConfigTest.php | 2 +- .../Test/Unit/Model/PayflowExpressTest.php | 2 +- .../Test/Unit/Model/PayflowlinkTest.php | 2 +- .../Paypal/Test/Unit/Model/PayflowproTest.php | 2 +- .../Method/Billing/AbstractAgreementStub.php | 2 +- .../Method/Billing/AbstractAgreementTest.php | 2 +- .../Paypal/Test/Unit/Model/ProTest.php | 2 +- .../Unit/Model/Report/Settlement/RowTest.php | 2 +- .../ResourceModel/Billing/AgreementTest.php | 2 +- .../System/Config/Source/BmlPositionTest.php | 2 +- .../Config/Source/YesnoshortcutTest.php | 2 +- .../Model/_files/additional_info_data.php | 2 +- ...dBillingAgreementToSessionObserverTest.php | 2 +- .../AddPaypalShortcutsObserverTest.php | 2 +- .../HtmlTransactionIdObserverTest.php | 2 +- ...AdminBillingAgreementUsageObserverTest.php | 2 +- .../SetResponseAfterSaveOrderObserverTest.php | 2 +- app/code/Magento/Paypal/composer.json | 2 +- app/code/Magento/Paypal/etc/acl.xml | 2 +- app/code/Magento/Paypal/etc/adminhtml/di.xml | 2 +- .../Magento/Paypal/etc/adminhtml/events.xml | 2 +- .../Magento/Paypal/etc/adminhtml/menu.xml | 2 +- .../Magento/Paypal/etc/adminhtml/routes.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_au.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_ca.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_de.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_es.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_fr.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_gb.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_hk.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_it.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_jp.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_nz.xml | 2 +- .../etc/adminhtml/rules/payment_other.xml | 2 +- .../Paypal/etc/adminhtml/rules/payment_us.xml | 2 +- .../Magento/Paypal/etc/adminhtml/system.xml | 2 +- .../etc/adminhtml/system/express_checkout.xml | 5 +- .../etc/adminhtml/system/payflow_advanced.xml | 2 +- .../etc/adminhtml/system/payflow_link.xml | 2 +- .../system/payments_pro_hosted_solution.xml | 2 +- ..._hosted_solution_with_express_checkout.xml | 2 +- .../adminhtml/system/paypal_payflowpro.xml | 2 +- ...aypal_payflowpro_with_express_checkout.xml | 2 +- app/code/Magento/Paypal/etc/config.xml | 2 +- app/code/Magento/Paypal/etc/crontab.xml | 2 +- app/code/Magento/Paypal/etc/di.xml | 30 +- app/code/Magento/Paypal/etc/events.xml | 2 +- app/code/Magento/Paypal/etc/frontend/di.xml | 2 +- .../Magento/Paypal/etc/frontend/events.xml | 2 +- .../Paypal/etc/frontend/page_types.xml | 2 +- .../Magento/Paypal/etc/frontend/routes.xml | 4 +- .../Magento/Paypal/etc/frontend/sections.xml | 2 +- app/code/Magento/Paypal/etc/module.xml | 2 +- app/code/Magento/Paypal/etc/payment.xml | 2 +- app/code/Magento/Paypal/etc/rules.xsd | 2 +- app/code/Magento/Paypal/registration.php | 2 +- .../layout/adminhtml_paypal_reports_block.xml | 2 +- .../layout/adminhtml_system_config_edit.xml | 2 +- .../adminhtml/layout/customer_index_edit.xml | 2 +- .../paypal_billing_agreement_customergrid.xml | 2 +- .../layout/paypal_billing_agreement_grid.xml | 2 +- .../layout/paypal_billing_agreement_index.xml | 2 +- .../paypal_billing_agreement_ordersgrid.xml | 2 +- .../layout/paypal_billing_agreement_view.xml | 2 +- .../layout/paypal_paypal_reports_grid.xml | 2 +- .../layout/paypal_paypal_reports_index.xml | 2 +- .../layout/sales_order_create_index.xml | 4 +- ...order_create_load_block_billing_method.xml | 4 +- .../layout/transparent_payment_response.xml | 2 +- .../templates/billing/agreement/form.phtml | 2 +- .../billing/agreement/view/form.phtml | 2 +- .../billing/agreement/view/tab/info.phtml | 2 +- .../templates/payflowpro/vault.phtml | 2 +- .../payment/form/billing/agreement.phtml | 2 +- .../templates/system/config/api_wizard.phtml | 2 +- .../system/config/bml_api_wizard.phtml | 2 +- .../system/config/fieldset/hint.phtml | 2 +- .../system/config/payflowlink/advanced.phtml | 2 +- .../system/config/payflowlink/info.phtml | 2 +- .../templates/system/config/rules.phtml | 2 +- .../templates/transparent/form.phtml | 6 +- .../templates/transparent/iframe.phtml | 2 +- .../view/adminhtml/web/js/payflowpro/vault.js | 2 +- .../adminhtml/web/js/predicate/confirm.js | 7 +- .../Paypal/view/adminhtml/web/js/rule.js | 2 +- .../Paypal/view/adminhtml/web/js/rules.js | 2 +- .../Paypal/view/adminhtml/web/js/solution.js | 2 +- .../Paypal/view/adminhtml/web/js/solutions.js | 2 +- .../Paypal/view/adminhtml/web/styles.css | 4 +- .../Paypal/view/base/requirejs-config.js | 6 +- .../frontend/layout/catalog_category_view.xml | 2 +- .../frontend/layout/catalog_product_view.xml | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 4 +- .../layout/checkout_onepage_review.xml | 2 +- .../layout/checkout_onepage_success.xml | 2 +- .../view/frontend/layout/cms_index_index.xml | 2 +- .../view/frontend/layout/customer_account.xml | 5 +- .../Paypal/view/frontend/layout/default.xml | 2 +- .../layout/paypal_billing_agreement_index.xml | 2 +- .../layout/paypal_billing_agreement_view.xml | 2 +- .../frontend/layout/paypal_express_review.xml | 2 +- .../layout/paypal_express_review_details.xml | 2 +- .../layout/paypal_payflow_cancelpayment.xml | 2 +- .../frontend/layout/paypal_payflow_form.xml | 2 +- .../layout/paypal_payflow_returnurl.xml | 2 +- .../paypal_payflowadvanced_cancelpayment.xml | 2 +- .../layout/paypal_payflowadvanced_form.xml | 2 +- .../paypal_payflowadvanced_returnurl.xml | 2 +- .../layout/paypal_payflowexpress_review.xml | 2 +- .../layout/transparent_payment_response.xml | 2 +- .../layout/vault_cards_listaction.xml | 2 +- .../Paypal/view/frontend/requirejs-config.js | 6 +- .../templates/billing/agreement/view.phtml | 2 +- .../templates/billing/agreements.phtml | 2 +- .../Paypal/view/frontend/templates/bml.phtml | 2 +- .../checkout/onepage/review/totals.phtml | 2 +- .../onepage/success/billing_agreement.phtml | 2 +- .../express/in-context/component.phtml | 2 +- .../express/in-context/shortcut/button.phtml | 2 +- .../frontend/templates/express/review.phtml | 2 +- .../templates/express/review/details.phtml | 2 +- .../express/review/shipping/method.phtml | 2 +- .../frontend/templates/express/shortcut.phtml | 2 +- .../express/shortcut/container.phtml | 2 +- .../view/frontend/templates/hss/form.phtml | 2 +- .../view/frontend/templates/hss/iframe.phtml | 2 +- .../view/frontend/templates/hss/info.phtml | 2 +- .../view/frontend/templates/hss/js.phtml | 2 +- .../templates/hss/review/button.phtml | 2 +- .../frontend/templates/js/components.phtml | 4 +- .../frontend/templates/partner/logo.phtml | 2 +- .../templates/payflowadvanced/form.phtml | 2 +- .../templates/payflowadvanced/info.phtml | 2 +- .../frontend/templates/payflowlink/form.phtml | 2 +- .../frontend/templates/payflowlink/info.phtml | 2 +- .../templates/payflowlink/redirect.phtml | 2 +- .../payment/form/billing/agreement.phtml | 2 +- .../frontend/templates/payment/mark.phtml | 2 +- .../frontend/templates/payment/redirect.phtml | 2 +- .../web/js/action/set-payment-method.js | 98 +- .../web/js/in-context/billing-agreement.js | 2 +- .../view/frontend/web/js/in-context/button.js | 2 +- .../web/js/in-context/express-checkout.js | 2 +- .../frontend/web/js/model/iframe-redirect.js | 49 +- .../view/frontend/web/js/model/iframe.js | 34 +- .../view/frontend/web/js/paypal-checkout.js | 4 +- .../payment/method-renderer/iframe-methods.js | 160 +- .../in-context/checkout-express.js | 22 +- .../method-renderer/payflow-express-bml.js | 28 +- .../method-renderer/payflow-express.js | 28 +- .../method-renderer/payflowpro-method.js | 267 +- .../method-renderer/payflowpro/vault.js | 2 +- .../paypal-billing-agreement.js | 105 +- .../paypal-express-abstract.js | 163 +- .../method-renderer/paypal-express-bml.js | 28 +- .../payment/method-renderer/paypal-express.js | 28 +- .../web/js/view/payment/paypal-payments.js | 113 +- .../web/js/view/review/actions/iframe.js | 86 +- .../Paypal/view/frontend/web/order-review.js | 192 +- .../payment/express/billing-agreement.html | 2 +- .../web/template/payment/iframe-methods.html | 2 +- .../template/payment/payflow-express-bml.html | 2 +- .../web/template/payment/payflow-express.html | 2 +- .../web/template/payment/payflowpro-form.html | 6 +- .../template/payment/paypal-express-bml.html | 2 +- .../payment/paypal-express-in-context.html | 2 +- .../web/template/payment/paypal-express.html | 2 +- .../paypal_billing_agreement-form.html | 2 +- .../template/payment/paypal_direct-form.html | 2 +- .../Persistent/Block/Form/Remember.php | 2 +- .../Persistent/Block/Header/Additional.php | 2 +- .../Magento/Persistent/Controller/Index.php | 2 +- .../Controller/Index/ExpressCheckout.php | 2 +- .../Controller/Index/SaveMethod.php | 2 +- .../Controller/Index/UnsetCookie.php | 2 +- app/code/Magento/Persistent/Helper/Data.php | 2 +- .../Magento/Persistent/Helper/Session.php | 2 +- .../Checkout/AddressDataProcessorPlugin.php | 2 +- .../Model/Checkout/ConfigProviderPlugin.php | 2 +- .../Model/CheckoutConfigProvider.php | 2 +- app/code/Magento/Persistent/Model/Factory.php | 2 +- .../Model/Layout/DepersonalizePlugin.php | 2 +- .../Magento/Persistent/Model/Observer.php | 11 +- .../Persistent/Model/Persistent/Config.php | 2 +- .../Persistent/Model/Plugin/CustomerData.php | 2 +- .../Magento/Persistent/Model/QuoteManager.php | 2 +- .../Model/ResourceModel/Session.php | 2 +- app/code/Magento/Persistent/Model/Session.php | 2 +- .../ApplyBlockPersistentDataObserver.php | 2 +- .../Observer/ApplyPersistentDataObserver.php | 2 +- .../CheckExpirePersistentQuoteObserver.php | 2 +- .../Observer/ClearExpiredCronJobObserver.php | 2 +- .../CustomerAuthenticatedEventObserver.php | 2 +- .../Observer/EmulateCustomerObserver.php | 2 +- .../Observer/EmulateQuoteObserver.php | 2 +- .../MakePersistentQuoteGuestObserver.php | 2 +- .../PreventClearCheckoutSessionObserver.php | 2 +- .../PreventExpressCheckoutObserver.php | 2 +- .../Observer/RefreshCustomerData.php | 2 +- .../RemovePersistentCookieObserver.php | 2 +- .../Observer/RenewCookieObserver.php | 2 +- .../SetLoadPersistentQuoteObserver.php | 2 +- .../SetQuotePersistentDataObserver.php | 2 +- .../SetRememberMeCheckedStatusObserver.php | 2 +- ...etRememberMeStatusForAjaxLoginObserver.php | 2 +- .../SynchronizePersistentInfoObserver.php | 2 +- .../SynchronizePersistentOnLoginObserver.php | 2 +- .../SynchronizePersistentOnLogoutObserver.php | 2 +- .../UpdateCustomerCookiesObserver.php | 2 +- .../Persistent/Setup/InstallSchema.php | 2 +- .../Test/Unit/Block/Header/AdditionalTest.php | 16 +- .../Persistent/Test/Unit/Helper/DataTest.php | 2 +- .../Checkout/ConfigProviderPluginTest.php | 2 +- .../Test/Unit/Model/FactoryTest.php | 2 +- .../Model/Layout/DepersonalizePluginTest.php | 2 +- .../Unit/Model/Plugin/CustomerDataTest.php | 2 +- .../Test/Unit/Model/QuoteManagerTest.php | 2 +- .../Test/Unit/Model/SessionTest.php | 2 +- .../ApplyBlockPersistentDataObserverTest.php | 2 +- .../ApplyPersistentDataObserverTest.php | 2 +- ...CheckExpirePersistentQuoteObserverTest.php | 2 +- .../ClearExpiredCronJobObserverTest.php | 2 +- ...CustomerAuthenticatedEventObserverTest.php | 2 +- .../Observer/EmulateCustomerObserverTest.php | 2 +- .../Observer/EmulateQuoteObserverTest.php | 2 +- .../MakePersistentQuoteGuestObserverTest.php | 2 +- ...reventClearCheckoutSessionObserverTest.php | 2 +- .../PreventExpressCheckoutObserverTest.php | 2 +- .../Unit/Observer/RefreshCustomerDataTest.php | 2 +- .../RemovePersistentCookieObserverTest.php | 2 +- .../Unit/Observer/RenewCookieObserverTest.php | 2 +- .../SetLoadPersistentQuoteObserverTest.php | 2 +- .../SetQuotePersistentDataObserverTest.php | 2 +- ...SetRememberMeCheckedStatusObserverTest.php | 2 +- .../SynchronizePersistentInfoObserverTest.php | 2 +- ...chronizePersistentOnLogoutObserverTest.php | 2 +- .../UpdateCustomerCookiesObserverTest.php | 2 +- app/code/Magento/Persistent/composer.json | 2 +- app/code/Magento/Persistent/etc/acl.xml | 2 +- .../Persistent/etc/adminhtml/system.xml | 2 +- app/code/Magento/Persistent/etc/config.xml | 2 +- app/code/Magento/Persistent/etc/crontab.xml | 2 +- app/code/Magento/Persistent/etc/di.xml | 2 +- .../Persistent/etc/extension_attributes.xml | 2 +- .../Magento/Persistent/etc/frontend/di.xml | 2 +- .../Persistent/etc/frontend/events.xml | 2 +- .../Persistent/etc/frontend/routes.xml | 4 +- app/code/Magento/Persistent/etc/module.xml | 2 +- .../Magento/Persistent/etc/persistent.xml | 4 +- .../Magento/Persistent/etc/persistent.xsd | 2 +- .../Persistent/etc/webapi_rest/events.xml | 2 +- .../Persistent/etc/webapi_soap/events.xml | 2 +- app/code/Magento/Persistent/registration.php | 2 +- .../layout/customer_account_create.xml | 2 +- .../layout/customer_account_login.xml | 2 +- .../view/frontend/templates/remember_me.phtml | 2 +- .../view/frontend/web/js/view/remember-me.js | 40 +- .../frontend/web/template/remember-me.html | 2 +- .../Block/Email/AbstractEmail.php | 2 +- .../ProductAlert/Block/Email/Price.php | 2 +- .../ProductAlert/Block/Email/Stock.php | 2 +- .../ProductAlert/Block/Product/View.php | 2 +- .../ProductAlert/Block/Product/View/Price.php | 2 +- .../ProductAlert/Block/Product/View/Stock.php | 2 +- .../Magento/ProductAlert/Controller/Add.php | 2 +- .../ProductAlert/Controller/Add/Price.php | 2 +- .../ProductAlert/Controller/Add/Stock.php | 2 +- .../Controller/Add/TestObserver.php | 2 +- .../ProductAlert/Controller/Unsubscribe.php | 2 +- .../Controller/Unsubscribe/Price.php | 2 +- .../Controller/Unsubscribe/PriceAll.php | 2 +- .../Controller/Unsubscribe/Stock.php | 2 +- .../Controller/Unsubscribe/StockAll.php | 2 +- app/code/Magento/ProductAlert/Helper/Data.php | 2 +- app/code/Magento/ProductAlert/Model/Email.php | 2 +- .../Magento/ProductAlert/Model/Observer.php | 2 +- app/code/Magento/ProductAlert/Model/Price.php | 2 +- .../Model/ResourceModel/AbstractResource.php | 2 +- .../Model/ResourceModel/Price.php | 2 +- .../Model/ResourceModel/Price/Collection.php | 2 +- .../Price/Customer/Collection.php | 2 +- .../Model/ResourceModel/Stock.php | 2 +- .../Model/ResourceModel/Stock/Collection.php | 2 +- .../Stock/Customer/Collection.php | 2 +- app/code/Magento/ProductAlert/Model/Stock.php | 2 +- .../ProductAlert/Setup/InstallSchema.php | 2 +- .../Magento/ProductAlert/Setup/Recurring.php | 2 +- .../Test/Unit/Block/Email/StockTest.php | 2 +- .../Unit/Block/Product/View/PriceTest.php | 2 +- .../Unit/Block/Product/View/StockTest.php | 2 +- .../Test/Unit/Block/Product/ViewTest.php | 2 +- .../Test/Unit/Model/ObserverTest.php | 2 +- app/code/Magento/ProductAlert/composer.json | 5 +- .../ProductAlert/etc/adminhtml/system.xml | 2 +- app/code/Magento/ProductAlert/etc/config.xml | 2 +- app/code/Magento/ProductAlert/etc/crontab.xml | 2 +- app/code/Magento/ProductAlert/etc/di.xml | 10 +- .../ProductAlert/etc/email_templates.xml | 2 +- .../ProductAlert/etc/frontend/routes.xml | 4 +- app/code/Magento/ProductAlert/etc/module.xml | 2 +- .../Magento/ProductAlert/registration.php | 2 +- .../view/adminhtml/email/cron_error.html | 2 +- .../view/frontend/email/price_alert.html | 2 +- .../view/frontend/email/stock_alert.html | 2 +- .../frontend/layout/catalog_product_view.xml | 2 +- .../view/frontend/templates/email/price.phtml | 2 +- .../view/frontend/templates/email/stock.phtml | 2 +- .../frontend/templates/product/view.phtml | 2 +- .../Block/Adminhtml/Product/Edit/NewVideo.php | 2 +- .../Block/Product/View/Gallery.php | 2 +- .../Product/Gallery/RetrieveImage.php | 55 +- .../Magento/ProductVideo/Helper/Media.php | 2 +- .../Product/Gallery/AbstractHandler.php | 2 +- .../Catalog/Product/Gallery/CreateHandler.php | 2 +- .../Catalog/Product/Gallery/ReadHandler.php | 2 +- .../Plugin/ExternalVideoResourceBackend.php | 2 +- .../Media/ExternalVideoEntryConverter.php | 2 +- .../Product/Attribute/Media/VideoEntry.php | 2 +- .../Model/ResourceModel/Video.php | 2 +- .../ProductVideo/Model/VideoExtractor.php | 2 +- .../Observer/ChangeTemplateObserver.php | 2 +- .../ProductVideo/Setup/InstallSchema.php | 4 +- .../Adminhtml/Product/Edit/NewVideoTest.php | 2 +- .../Unit/Block/Product/View/GalleryTest.php | 2 +- .../Product/Gallery/RetrieveImageTest.php | 23 +- .../Test/Unit/Helper/MediaTest.php | 2 +- .../Product/Gallery/CreateHandlerTest.php | 2 +- .../Product/Gallery/ReadHandlerTest.php | 2 +- .../Media/ExternalVideoEntryConverterTest.php | 2 +- .../Attribute/Media/VideoEntryTest.php | 2 +- .../Observer/ChangeTemplateObserverTest.php | 2 +- app/code/Magento/ProductVideo/composer.json | 5 +- .../ProductVideo/etc/adminhtml/events.xml | 2 +- .../ProductVideo/etc/adminhtml/routes.xml | 2 +- .../ProductVideo/etc/adminhtml/system.xml | 2 +- app/code/Magento/ProductVideo/etc/config.xml | 2 +- app/code/Magento/ProductVideo/etc/di.xml | 10 +- .../ProductVideo/etc/extension_attributes.xml | 4 +- app/code/Magento/ProductVideo/etc/module.xml | 2 +- app/code/Magento/ProductVideo/i18n/en_US.csv | 1 + .../Magento/ProductVideo/registration.php | 2 +- .../adminhtml/layout/catalog_product_form.xml | 2 +- .../adminhtml/layout/catalog_product_new.xml | 2 +- .../view/adminhtml/requirejs-config.js | 4 +- .../adminhtml/templates/helper/gallery.phtml | 2 +- .../templates/product/edit/base_image.phtml | 2 +- .../product/edit/slideout/form.phtml | 2 +- .../adminhtml/web/js/get-video-information.js | 5 +- .../view/adminhtml/web/js/new-video-dialog.js | 2 +- .../view/adminhtml/web/js/video-modal.js | 2 +- .../frontend/layout/catalog_product_view.xml | 2 +- .../view/frontend/requirejs-config.js | 4 +- .../templates/product/view/gallery.phtml | 2 +- .../web/js/fotorama-add-video-events.js | 2 +- .../view/frontend/web/js/load-player.js | 4 +- .../Api/BillingAddressManagementInterface.php | 2 +- .../Quote/Api/CartItemRepositoryInterface.php | 2 +- .../Quote/Api/CartManagementInterface.php | 2 +- .../Quote/Api/CartRepositoryInterface.php | 2 +- .../Api/CartTotalManagementInterface.php | 2 +- .../Api/CartTotalRepositoryInterface.php | 2 +- .../Quote/Api/CouponManagementInterface.php | 2 +- .../Data/AddressAdditionalDataInterface.php | 2 +- .../Quote/Api/Data/AddressInterface.php | 4 +- .../Magento/Quote/Api/Data/CartInterface.php | 2 +- .../Quote/Api/Data/CartItemInterface.php | 2 +- .../Api/Data/CartSearchResultsInterface.php | 2 +- .../Quote/Api/Data/CurrencyInterface.php | 2 +- .../Api/Data/EstimateAddressInterface.php | 2 +- .../Quote/Api/Data/PaymentInterface.php | 2 +- .../Quote/Api/Data/PaymentMethodInterface.php | 2 +- .../Quote/Api/Data/ProductOptionInterface.php | 2 +- .../Api/Data/ShippingAssignmentInterface.php | 2 +- .../Quote/Api/Data/ShippingInterface.php | 2 +- .../Api/Data/ShippingMethodInterface.php | 2 +- .../Quote/Api/Data/TotalSegmentInterface.php | 2 +- .../Data/TotalsAdditionalDataInterface.php | 2 +- .../Quote/Api/Data/TotalsInterface.php | 2 +- .../Quote/Api/Data/TotalsItemInterface.php | 2 +- ...GuestBillingAddressManagementInterface.php | 5 +- .../Api/GuestCartItemRepositoryInterface.php | 2 +- .../Api/GuestCartManagementInterface.php | 2 +- .../Api/GuestCartRepositoryInterface.php | 2 +- .../Api/GuestCartTotalManagementInterface.php | 2 +- .../Api/GuestCartTotalRepositoryInterface.php | 2 +- .../Api/GuestCouponManagementInterface.php | 2 +- .../GuestPaymentMethodManagementInterface.php | 2 +- .../Api/GuestShipmentEstimationInterface.php | 2 +- ...GuestShippingMethodManagementInterface.php | 2 +- .../Api/PaymentMethodManagementInterface.php | 2 +- .../Quote/Api/ShipmentEstimationInterface.php | 2 +- .../Api/ShippingMethodManagementInterface.php | 2 +- .../Quote/Model/AddressAdditionalData.php | 2 +- .../Model/AddressAdditionalDataProcessor.php | 2 +- .../Quote/Model/BillingAddressManagement.php | 21 +- .../Quote/Model/Cart/CartTotalManagement.php | 2 +- .../Quote/Model/Cart/CartTotalRepository.php | 2 +- .../Magento/Quote/Model/Cart/Currency.php | 2 +- .../Quote/Model/Cart/ShippingMethod.php | 2 +- .../Model/Cart/ShippingMethodConverter.php | 2 +- .../Magento/Quote/Model/Cart/TotalSegment.php | 2 +- app/code/Magento/Quote/Model/Cart/Totals.php | 2 +- .../Magento/Quote/Model/Cart/Totals/Item.php | 2 +- .../Quote/Model/Cart/Totals/ItemConverter.php | 2 +- .../Quote/Model/Cart/TotalsAdditionalData.php | 2 +- .../Cart/TotalsAdditionalDataProcessor.php | 2 +- .../Quote/Model/Cart/TotalsConverter.php | 2 +- .../Magento/Quote/Model/CouponManagement.php | 2 +- .../Quote/Model/CustomerManagement.php | 4 +- .../Magento/Quote/Model/EstimateAddress.php | 2 +- .../GuestBillingAddressManagement.php | 6 +- .../GuestCart/GuestCartItemRepository.php | 2 +- .../Model/GuestCart/GuestCartManagement.php | 2 +- .../Model/GuestCart/GuestCartRepository.php | 2 +- .../GuestCart/GuestCartTotalManagement.php | 2 +- .../GuestCart/GuestCartTotalRepository.php | 2 +- .../Model/GuestCart/GuestCouponManagement.php | 2 +- .../GuestPaymentMethodManagement.php | 2 +- .../GuestShippingAddressManagement.php | 2 +- ...uestShippingAddressManagementInterface.php | 2 +- .../GuestShippingMethodManagement.php | 2 +- ...GuestShippingMethodManagementInterface.php | 2 +- .../Plugin/Authorization.php | 2 +- .../Quote/Model/PaymentMethodManagement.php | 2 +- .../Model/Product/Plugin/RemoveQuoteItems.php | 18 +- .../Model/Product/Plugin/UpdateQuoteItems.php | 2 +- .../Quote/Model/Product/QuoteItemsCleaner.php | 2 +- .../Product/QuoteItemsCleanerInterface.php | 2 +- .../Magento/Quote/Model/QueryResolver.php | 18 +- app/code/Magento/Quote/Model/Quote.php | 14 +- .../Magento/Quote/Model/Quote/Address.php | 19 +- .../Quote/Address/BillingAddressPersister.php | 2 +- .../Quote/Address/CustomAttributeList.php | 2 +- .../Address/CustomAttributeListInterface.php | 2 +- .../Model/Quote/Address/FreeShipping.php | 2 +- .../Quote/Address/FreeShippingInterface.php | 2 +- .../Quote/Model/Quote/Address/Item.php | 2 +- .../Quote/Model/Quote/Address/Rate.php | 2 +- .../Quote/Address/RateCollectorInterface.php | 2 +- .../Address/RateCollectorInterfaceFactory.php | 2 +- .../Quote/Model/Quote/Address/RateRequest.php | 2 +- .../Address/RateResult/AbstractResult.php | 2 +- .../Model/Quote/Address/RateResult/Error.php | 2 +- .../Model/Quote/Address/RateResult/Method.php | 2 +- .../Quote/Model/Quote/Address/Relation.php | 2 +- .../Quote/Model/Quote/Address/ToOrder.php | 2 +- .../Model/Quote/Address/ToOrderAddress.php | 2 +- .../Quote/Model/Quote/Address/Total.php | 26 +- .../Quote/Address/Total/AbstractTotal.php | 2 +- .../Model/Quote/Address/Total/Collector.php | 10 +- .../Address/Total/CollectorInterface.php | 2 +- .../Quote/Model/Quote/Address/Total/Grand.php | 10 +- .../Quote/Address/Total/ReaderInterface.php | 2 +- .../Model/Quote/Address/Total/Shipping.php | 16 +- .../Model/Quote/Address/Total/Subtotal.php | 2 +- .../Model/Quote/Address/TotalFactory.php | 2 +- .../Quote/Model/Quote/Address/Validator.php | 2 +- app/code/Magento/Quote/Model/Quote/Config.php | 2 +- app/code/Magento/Quote/Model/Quote/Item.php | 18 +- .../Quote/Model/Quote/Item/AbstractItem.php | 2 +- .../Quote/Item/CartItemOptionsProcessor.php | 2 +- .../Model/Quote/Item/CartItemPersister.php | 2 +- .../Quote/Item/CartItemProcessorInterface.php | 2 +- .../Quote/Item/CartItemProcessorsPool.php | 2 +- .../Quote/Model/Quote/Item/Compare.php | 22 +- .../Magento/Quote/Model/Quote/Item/Option.php | 2 +- .../Quote/Model/Quote/Item/Processor.php | 2 +- .../Model/Quote/Item/RelatedProducts.php | 2 +- .../Quote/Model/Quote/Item/Repository.php | 2 +- .../Quote/Model/Quote/Item/ToOrderItem.php | 2 +- .../Quote/Model/Quote/Item/Updater.php | 19 +- .../Magento/Quote/Model/Quote/Payment.php | 28 +- .../Model/Quote/Payment/ToOrderPayment.php | 2 +- .../Quote/Model/Quote/ProductOption.php | 2 +- .../Magento/Quote/Model/Quote/Relation.php | 2 +- .../ShippingAssignmentPersister.php | 2 +- .../ShippingAssignmentProcessor.php | 36 +- .../ShippingAssignment/ShippingProcessor.php | 2 +- .../Quote/Model/Quote/TotalsCollector.php | 2 +- .../Quote/Model/Quote/TotalsCollectorList.php | 2 +- .../Quote/Model/Quote/TotalsReader.php | 2 +- .../MinimumOrderAmount/ValidationMessage.php | 9 +- .../Quote/Model/QuoteAddressValidator.php | 2 +- app/code/Magento/Quote/Model/QuoteIdMask.php | 2 +- .../Magento/Quote/Model/QuoteManagement.php | 2 +- .../Magento/Quote/Model/QuoteRepository.php | 2 +- .../Model/QuoteRepository/LoadHandler.php | 2 +- .../QuoteRepository/Plugin/Authorization.php | 2 +- .../Model/QuoteRepository/SaveHandler.php | 39 +- .../Magento/Quote/Model/QuoteValidator.php | 2 +- .../Quote/Model/ResourceModel/Quote.php | 25 +- .../Model/ResourceModel/Quote/Address.php | 2 +- .../Quote/Address/Attribute/Backend.php | 2 +- .../Quote/Address/Attribute/Backend/Child.php | 2 +- .../Address/Attribute/Backend/Region.php | 2 +- .../Quote/Address/Attribute/Frontend.php | 2 +- .../Attribute/Frontend/Custbalance.php | 2 +- .../Address/Attribute/Frontend/Discount.php | 2 +- .../Address/Attribute/Frontend/Grand.php | 2 +- .../Address/Attribute/Frontend/Shipping.php | 2 +- .../Address/Attribute/Frontend/Subtotal.php | 2 +- .../Quote/Address/Attribute/Frontend/Tax.php | 2 +- .../Quote/Address/Collection.php | 2 +- .../ResourceModel/Quote/Address/Item.php | 2 +- .../Quote/Address/Item/Collection.php | 2 +- .../ResourceModel/Quote/Address/Rate.php | 2 +- .../Quote/Address/Rate/Collection.php | 2 +- .../Model/ResourceModel/Quote/Collection.php | 2 +- .../Quote/Model/ResourceModel/Quote/Item.php | 2 +- .../ResourceModel/Quote/Item/Collection.php | 2 +- .../Model/ResourceModel/Quote/Item/Option.php | 2 +- .../Quote/Item/Option/Collection.php | 2 +- .../Model/ResourceModel/Quote/Payment.php | 2 +- .../Quote/Payment/Collection.php | 2 +- .../Model/ResourceModel/Quote/QuoteIdMask.php | 2 +- app/code/Magento/Quote/Model/Shipping.php | 2 +- .../Quote/Model/ShippingAddressAssignment.php | 63 + .../Quote/Model/ShippingAddressManagement.php | 2 +- .../ShippingAddressManagementInterface.php | 2 +- .../Quote/Model/ShippingAssignment.php | 2 +- .../Quote/Model/ShippingMethodManagement.php | 2 +- .../ShippingMethodManagementInterface.php | 2 +- .../Model/Webapi/ParamOverriderCartId.php | 2 +- .../Backend/CustomerQuoteObserver.php | 2 +- .../Quote/Address/CollectTotalsObserver.php | 2 +- .../Frontend/Quote/Address/VatValidator.php | 2 +- .../Quote/Observer/Webapi/SubmitObserver.php | 2 +- .../Setup/ConvertSerializedDataToJson.php | 140 + app/code/Magento/Quote/Setup/InstallData.php | 2 +- .../Magento/Quote/Setup/InstallSchema.php | 2 +- app/code/Magento/Quote/Setup/QuoteSetup.php | 39 +- app/code/Magento/Quote/Setup/UpgradeData.php | 49 + .../Magento/Quote/Setup/UpgradeSchema.php | 2 +- .../Model/BillingAddressManagementTest.php | 143 +- .../Model/Cart/CartTotalManagementTest.php | 2 +- .../Model/Cart/CartTotalRepositoryTest.php | 2 +- .../Cart/ShippingMethodConverterTest.php | 2 +- .../Model/Cart/Totals/ItemConverterTest.php | 2 +- .../Test/Unit/Model/CouponManagementTest.php | 2 +- .../Unit/Model/CustomerManagementTest.php | 32 +- .../GuestBillingAddressManagementTest.php | 2 +- .../GuestCart/GuestCartItemRepositoryTest.php | 2 +- .../GuestCart/GuestCartManagementTest.php | 2 +- .../GuestCart/GuestCartRepositoryTest.php | 2 +- .../Model/GuestCart/GuestCartTestHelper.php | 2 +- .../GuestCartTotalRepositoryTest.php | 2 +- .../GuestCart/GuestCouponManagementTest.php | 2 +- .../GuestPaymentMethodManagementTest.php | 2 +- .../GuestShippingAddressManagementTest.php | 2 +- .../GuestShippingMethodManagementTest.php | 2 +- .../Plugin/AuthorizationTest.php | 2 +- .../Model/PaymentMethodManagementTest.php | 2 +- .../Product/Plugin/RemoveQuoteItemsTest.php | 9 +- .../Product/Plugin/UpdateQuoteItemsTest.php | 2 +- .../Model/Product/QuoteItemsCleanerTest.php | 2 +- .../Test/Unit/Model/QueryResolverTest.php | 77 +- .../Unit/Model/Quote/Address/RelationTest.php | 2 +- .../Quote/Address/ToOrderAddressTest.php | 2 +- .../Unit/Model/Quote/Address/ToOrderTest.php | 2 +- .../Model/Quote/Address/Total/GrandTest.php | 17 +- .../Quote/Address/Total/ShippingTest.php | 2 +- .../Quote/Address/Total/SubtotalTest.php | 2 +- .../Unit/Model/Quote/Address/TotalTest.php | 22 +- .../Model/Quote/Address/ValidatorTest.php | 2 +- .../Test/Unit/Model/Quote/AddressTest.php | 30 +- .../Test/Unit/Model/Quote/ConfigTest.php | 2 +- .../Model/Quote/Item/AbstractItemTest.php | 2 +- .../Unit/Model/Quote/Item/CompareTest.php | 37 +- .../Unit/Model/Quote/Item/ProcessorTest.php | 2 +- .../Model/Quote/Item/RelatedProductsTest.php | 2 +- .../Unit/Model/Quote/Item/RepositoryTest.php | 465 +- .../Unit/Model/Quote/Item/ToOrderItemTest.php | 2 +- .../Unit/Model/Quote/Item/UpdaterTest.php | 35 +- .../Quote/Test/Unit/Model/Quote/ItemTest.php | 21 +- .../Quote/Payment/ToOrderPaymentTest.php | 2 +- .../Test/Unit/Model/Quote/PaymentTest.php | 62 +- .../Test/Unit/Model/Quote/RelationTest.php | 2 +- .../ShippingAssignmentProcessorTest.php | 208 +- .../ShippingProcessorTest.php | 2 +- .../Unit/Model/Quote/TotalsReaderTest.php | 2 +- .../ValidationMessageTest.php | 10 +- .../Unit/Model/QuoteAddressValidatorTest.php | 55 +- .../Quote/Test/Unit/Model/QuoteIdMaskTest.php | 2 +- .../Test/Unit/Model/QuoteManagementTest.php | 2 +- .../Plugin/AuthorizationTest.php | 2 +- .../Model/QuoteRepository/SaveHandlerTest.php | 248 +- .../Test/Unit/Model/QuoteRepositoryTest.php | 91 +- .../Quote/Test/Unit/Model/QuoteTest.php | 41 +- .../Test/Unit/Model/QuoteValidatorTest.php | 2 +- .../Quote/Item/CollectionTest.php | 2 +- .../Model/ResourceModel/Quote/ItemTest.php | 2 +- .../ResourceModel/Quote/QuoteAddressTest.php | 2 +- .../Unit/Model/ResourceModel/QuoteTest.php | 84 + .../Model/ShippingAddressAssignmentTest.php | 115 + .../Model/ShippingAddressManagementTest.php | 213 +- .../Model/ShippingMethodManagementTest.php | 90 +- .../Model/Webapi/ParamOverriderCartIdTest.php | 2 +- .../Backend/CustomerQuoteObserverTest.php | 2 +- .../Address/CollectTotalsObserverTest.php | 2 +- .../Quote/Address/VatValidatorTest.php | 2 +- .../Observer/Webapi/SubmitObserverTest.php | 2 +- app/code/Magento/Quote/composer.json | 2 +- app/code/Magento/Quote/etc/acl.xml | 2 +- .../Magento/Quote/etc/adminhtml/events.xml | 2 +- app/code/Magento/Quote/etc/di.xml | 2 +- app/code/Magento/Quote/etc/events.xml | 2 +- .../Quote/etc/extension_attributes.xml | 2 +- app/code/Magento/Quote/etc/fieldset.xml | 2 +- app/code/Magento/Quote/etc/module.xml | 4 +- app/code/Magento/Quote/etc/sales.xml | 2 +- app/code/Magento/Quote/etc/webapi.xml | 2 +- app/code/Magento/Quote/etc/webapi_rest/di.xml | 2 +- .../Magento/Quote/etc/webapi_rest/events.xml | 4 +- app/code/Magento/Quote/etc/webapi_soap/di.xml | 2 +- .../Magento/Quote/etc/webapi_soap/events.xml | 4 +- app/code/Magento/Quote/registration.php | 2 +- .../Adminhtml/Config/Form/Field/MtdStart.php | 2 +- .../Adminhtml/Config/Form/Field/YtdStart.php | 2 +- .../Block/Adminhtml/Customer/Accounts.php | 2 +- .../Block/Adminhtml/Customer/Orders.php | 2 +- .../Block/Adminhtml/Customer/Totals.php | 2 +- .../Reports/Block/Adminhtml/Filter/Form.php | 2 +- .../Magento/Reports/Block/Adminhtml/Grid.php | 2 +- .../Block/Adminhtml/Grid/AbstractGrid.php | 2 +- .../Grid/Column/Renderer/Blanknumber.php | 2 +- .../Grid/Column/Renderer/Currency.php | 2 +- .../Grid/Column/Renderer/Customer.php | 2 +- .../Grid/Column/Renderer/Product.php | 2 +- .../Reports/Block/Adminhtml/Grid/Shopcart.php | 2 +- .../Reports/Block/Adminhtml/Product.php | 2 +- .../Block/Adminhtml/Product/Downloads.php | 2 +- .../Adminhtml/Product/Downloads/Grid.php | 2 +- .../Product/Downloads/Renderer/Purchases.php | 2 +- .../Block/Adminhtml/Product/Lowstock.php | 2 +- .../Block/Adminhtml/Product/Lowstock/Grid.php | 2 +- .../Reports/Block/Adminhtml/Product/Sold.php | 2 +- .../Block/Adminhtml/Product/Viewed.php | 2 +- .../Block/Adminhtml/Product/Viewed/Grid.php | 2 +- .../Block/Adminhtml/Refresh/Statistics.php | 2 +- .../Block/Adminhtml/Review/Customer.php | 2 +- .../Reports/Block/Adminhtml/Review/Detail.php | 2 +- .../Block/Adminhtml/Review/Detail/Grid.php | 2 +- .../Block/Adminhtml/Review/Product.php | 2 +- .../Block/Adminhtml/Sales/Bestsellers.php | 2 +- .../Adminhtml/Sales/Bestsellers/Grid.php | 2 +- .../Reports/Block/Adminhtml/Sales/Coupons.php | 2 +- .../Block/Adminhtml/Sales/Coupons/Grid.php | 2 +- .../Sales/Grid/Column/Renderer/Date.php | 2 +- .../Block/Adminhtml/Sales/Invoiced.php | 2 +- .../Block/Adminhtml/Sales/Invoiced/Grid.php | 2 +- .../Block/Adminhtml/Sales/Refunded.php | 2 +- .../Block/Adminhtml/Sales/Refunded/Grid.php | 2 +- .../Reports/Block/Adminhtml/Sales/Sales.php | 2 +- .../Block/Adminhtml/Sales/Sales/Grid.php | 2 +- .../Block/Adminhtml/Sales/Shipping.php | 2 +- .../Block/Adminhtml/Sales/Shipping/Grid.php | 2 +- .../Reports/Block/Adminhtml/Sales/Tax.php | 2 +- .../Block/Adminhtml/Sales/Tax/Grid.php | 2 +- .../Block/Adminhtml/Shopcart/Abandoned.php | 2 +- .../Adminhtml/Shopcart/Abandoned/Grid.php | 2 +- .../Block/Adminhtml/Shopcart/Customer.php | 2 +- .../Adminhtml/Shopcart/Customer/Grid.php | 2 +- .../Block/Adminhtml/Shopcart/Product.php | 2 +- .../Block/Adminhtml/Shopcart/Product/Grid.php | 2 +- .../Reports/Block/Adminhtml/Wishlist.php | 2 +- .../Reports/Block/Adminhtml/Wishlist/Grid.php | 2 +- .../Reports/Block/Product/AbstractProduct.php | 2 +- .../Reports/Block/Product/Compared.php | 2 +- .../Magento/Reports/Block/Product/Viewed.php | 2 +- .../Reports/Block/Product/Widget/Compared.php | 2 +- .../Reports/Block/Product/Widget/Viewed.php | 2 +- .../Block/Product/Widget/Viewed/Item.php | 2 +- .../Reports/Controller/Adminhtml/Index.php | 2 +- .../Adminhtml/Report/AbstractReport.php | 2 +- .../Controller/Adminhtml/Report/Customer.php | 2 +- .../Adminhtml/Report/Customer/Accounts.php | 2 +- .../Report/Customer/ExportAccountsCsv.php | 2 +- .../Report/Customer/ExportAccountsExcel.php | 2 +- .../Report/Customer/ExportOrdersCsv.php | 2 +- .../Report/Customer/ExportOrdersExcel.php | 2 +- .../Report/Customer/ExportTotalsCsv.php | 2 +- .../Report/Customer/ExportTotalsExcel.php | 2 +- .../Adminhtml/Report/Customer/Orders.php | 2 +- .../Adminhtml/Report/Customer/Totals.php | 2 +- .../Controller/Adminhtml/Report/Product.php | 2 +- .../Adminhtml/Report/Product/Downloads.php | 2 +- .../Report/Product/ExportDownloadsCsv.php | 2 +- .../Report/Product/ExportDownloadsExcel.php | 2 +- .../Report/Product/ExportLowstockCsv.php | 2 +- .../Report/Product/ExportLowstockExcel.php | 2 +- .../Report/Product/ExportSoldCsv.php | 2 +- .../Report/Product/ExportSoldExcel.php | 2 +- .../Report/Product/ExportViewedCsv.php | 2 +- .../Report/Product/ExportViewedExcel.php | 2 +- .../Adminhtml/Report/Product/Lowstock.php | 2 +- .../Adminhtml/Report/Product/Sold.php | 2 +- .../Adminhtml/Report/Product/Viewed.php | 2 +- .../Controller/Adminhtml/Report/Review.php | 2 +- .../Adminhtml/Report/Review/Customer.php | 2 +- .../Report/Review/ExportCustomerCsv.php | 2 +- .../Report/Review/ExportCustomerExcel.php | 2 +- .../Report/Review/ExportProductCsv.php | 2 +- .../Report/Review/ExportProductDetailCsv.php | 2 +- .../Review/ExportProductDetailExcel.php | 2 +- .../Report/Review/ExportProductExcel.php | 2 +- .../Adminhtml/Report/Review/Product.php | 2 +- .../Adminhtml/Report/Review/ProductDetail.php | 2 +- .../Controller/Adminhtml/Report/Sales.php | 2 +- .../Adminhtml/Report/Sales/Bestsellers.php | 2 +- .../Adminhtml/Report/Sales/Coupons.php | 2 +- .../Report/Sales/ExportBestsellersCsv.php | 2 +- .../Report/Sales/ExportBestsellersExcel.php | 2 +- .../Report/Sales/ExportCouponsCsv.php | 2 +- .../Report/Sales/ExportCouponsExcel.php | 2 +- .../Report/Sales/ExportInvoicedCsv.php | 2 +- .../Report/Sales/ExportInvoicedExcel.php | 2 +- .../Report/Sales/ExportRefundedCsv.php | 2 +- .../Report/Sales/ExportRefundedExcel.php | 2 +- .../Adminhtml/Report/Sales/ExportSalesCsv.php | 2 +- .../Report/Sales/ExportSalesExcel.php | 2 +- .../Report/Sales/ExportShippingCsv.php | 2 +- .../Report/Sales/ExportShippingExcel.php | 2 +- .../Adminhtml/Report/Sales/ExportTaxCsv.php | 2 +- .../Adminhtml/Report/Sales/ExportTaxExcel.php | 2 +- .../Adminhtml/Report/Sales/Invoiced.php | 2 +- .../Report/Sales/RefreshLifetime.php | 2 +- .../Adminhtml/Report/Sales/RefreshRecent.php | 2 +- .../Report/Sales/RefreshStatistics.php | 2 +- .../Adminhtml/Report/Sales/Refunded.php | 2 +- .../Adminhtml/Report/Sales/Sales.php | 2 +- .../Adminhtml/Report/Sales/Shipping.php | 2 +- .../Controller/Adminhtml/Report/Sales/Tax.php | 2 +- .../Controller/Adminhtml/Report/Shopcart.php | 2 +- .../Adminhtml/Report/Shopcart/Abandoned.php | 2 +- .../Adminhtml/Report/Shopcart/Customer.php | 2 +- .../Report/Shopcart/ExportAbandonedCsv.php | 2 +- .../Report/Shopcart/ExportAbandonedExcel.php | 2 +- .../Report/Shopcart/ExportCustomerCsv.php | 2 +- .../Report/Shopcart/ExportCustomerExcel.php | 2 +- .../Report/Shopcart/ExportProductCsv.php | 2 +- .../Report/Shopcart/ExportProductExcel.php | 2 +- .../Adminhtml/Report/Shopcart/Product.php | 2 +- .../Adminhtml/Report/Statistics.php | 2 +- .../Adminhtml/Report/Statistics/Index.php | 2 +- .../Report/Statistics/RefreshLifetime.php | 2 +- .../Report/Statistics/RefreshRecent.php | 2 +- app/code/Magento/Reports/Helper/Data.php | 2 +- app/code/Magento/Reports/Model/Config.php | 2 +- app/code/Magento/Reports/Model/Event.php | 2 +- app/code/Magento/Reports/Model/Event/Type.php | 2 +- app/code/Magento/Reports/Model/Flag.php | 2 +- .../Reports/Model/Grouped/Collection.php | 2 +- app/code/Magento/Reports/Model/Item.php | 2 +- app/code/Magento/Reports/Model/Plugin/Log.php | 2 +- .../Model/Product/Index/AbstractIndex.php | 2 +- .../Reports/Model/Product/Index/Compared.php | 2 +- .../Reports/Model/Product/Index/Factory.php | 2 +- .../Reports/Model/Product/Index/Viewed.php | 2 +- .../ResourceModel/Accounts/Collection.php | 2 +- .../Accounts/Collection/Initial.php | 2 +- .../ResourceModel/Customer/Collection.php | 2 +- .../Customer/Orders/Collection.php | 2 +- .../Customer/Orders/Collection/Initial.php | 2 +- .../Customer/Totals/Collection.php | 2 +- .../Customer/Totals/Collection/Initial.php | 2 +- .../Reports/Model/ResourceModel/Event.php | 2 +- .../Model/ResourceModel/Event/Collection.php | 2 +- .../Model/ResourceModel/Event/Type.php | 2 +- .../ResourceModel/Event/Type/Collection.php | 2 +- .../Reports/Model/ResourceModel/Helper.php | 2 +- .../Model/ResourceModel/HelperInterface.php | 2 +- .../Model/ResourceModel/Order/Collection.php | 2 +- .../ResourceModel/Product/Collection.php | 2 +- .../Product/Downloads/Collection.php | 2 +- .../Product/Index/AbstractIndex.php | 2 +- .../Index/Collection/AbstractCollection.php | 2 +- .../ResourceModel/Product/Index/Compared.php | 2 +- .../Product/Index/Compared/Collection.php | 2 +- .../ResourceModel/Product/Index/Viewed.php | 2 +- .../Product/Index/Viewed/Collection.php | 2 +- .../Product/Lowstock/Collection.php | 2 +- .../ResourceModel/Product/Sold/Collection.php | 2 +- .../Product/Sold/Collection/Initial.php | 2 +- .../Model/ResourceModel/Quote/Collection.php | 2 +- .../ResourceModel/Quote/CollectionFactory.php | 2 +- .../Quote/CollectionFactoryInterface.php | 2 +- .../ResourceModel/Quote/Item/Collection.php | 2 +- .../ResourceModel/Refresh/Collection.php | 2 +- .../ResourceModel/Report/AbstractReport.php | 2 +- .../Model/ResourceModel/Report/Collection.php | 2 +- .../Report/Collection/AbstractCollection.php | 2 +- .../Report/Collection/Factory.php | 2 +- .../ResourceModel/Report/Product/Viewed.php | 2 +- .../Report/Product/Viewed/Collection.php | 2 +- .../Model/ResourceModel/Review/Collection.php | 2 +- .../Review/Customer/Collection.php | 2 +- .../Review/Product/Collection.php | 2 +- .../ResourceModel/Wishlist/Collection.php | 2 +- .../Wishlist/Product/Collection.php | 2 +- ...atalogProductCompareAddProductObserver.php | 2 +- .../CatalogProductCompareClearObserver.php | 2 +- .../Observer/CatalogProductViewObserver.php | 2 +- .../CheckoutCartAddProductObserver.php | 2 +- .../Observer/CustomerLoginObserver.php | 2 +- .../Observer/CustomerLogoutObserver.php | 2 +- .../Magento/Reports/Observer/EventSaver.php | 2 +- .../Observer/SendfriendProductObserver.php | 2 +- .../Observer/WishlistAddProductObserver.php | 2 +- .../Observer/WishlistShareObserver.php | 2 +- .../Magento/Reports/Setup/InstallData.php | 2 +- .../Magento/Reports/Setup/InstallSchema.php | 2 +- app/code/Magento/Reports/Setup/Recurring.php | 2 +- .../Sales/Grid/Column/Renderer/DateTest.php | 2 +- .../Test/Unit/Block/Product/ComparedTest.php | 2 +- .../Test/Unit/Block/Product/ViewedTest.php | 2 +- .../Report/AbstractControllerTest.php | 2 +- .../Report/Customer/AccountsTest.php | 2 +- .../Report/Customer/ExportAccountsCsvTest.php | 2 +- .../Customer/ExportAccountsExcelTest.php | 2 +- .../Report/Customer/ExportOrdersCsvTest.php | 2 +- .../Report/Customer/ExportOrdersExcelTest.php | 2 +- .../Report/Customer/ExportTotalsCsvTest.php | 2 +- .../Report/Customer/ExportTotalsExcelTest.php | 2 +- .../Adminhtml/Report/Customer/OrdersTest.php | 2 +- .../Adminhtml/Report/Customer/TotalsTest.php | 2 +- .../Report/Product/DownloadsTest.php | 2 +- .../Report/Product/ExportDownloadsCsvTest.php | 2 +- .../Product/ExportDownloadsExcelTest.php | 2 +- .../Report/Product/ExportLowstockCsvTest.php | 2 +- .../Product/ExportLowstockExcelTest.php | 2 +- .../Report/Product/ExportSoldCsvTest.php | 2 +- .../Report/Product/ExportSoldExcelTest.php | 2 +- .../Report/Product/ExportViewedCsvTest.php | 2 +- .../Report/Product/ExportViewedExcelTest.php | 2 +- .../Adminhtml/Report/Product/LowstockTest.php | 2 +- .../Adminhtml/Report/Product/SoldTest.php | 2 +- .../Adminhtml/Report/Product/ViewedTest.php | 2 +- .../Reports/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Model/Plugin/LogTest.php | 2 +- .../Unit/Model/Product/Index/ComparedTest.php | 2 +- .../ResourceModel/Event/CollectionTest.php | 2 +- .../Unit/Model/ResourceModel/EventTest.php | 2 +- .../Unit/Model/ResourceModel/HelperTest.php | 2 +- .../ResourceModel/Order/CollectionTest.php | 2 +- .../ResourceModel/Quote/CollectionTest.php | 2 +- .../Collection/AbstractCollectionTest.php | 2 +- .../ResourceModel/Report/CollectionTest.php | 2 +- .../Report/Product/ViewedTest.php | 2 +- .../Report/Quote/CollectionTest.php | 2 +- ...ogProductCompareAddProductObserverTest.php | 2 +- .../CatalogProductViewObserverTest.php | 2 +- .../Observer/CustomerLoginObserverTest.php | 2 +- .../Observer/CustomerLogoutObserverTest.php | 2 +- app/code/Magento/Reports/composer.json | 2 +- app/code/Magento/Reports/etc/acl.xml | 2 +- app/code/Magento/Reports/etc/adminhtml/di.xml | 2 +- .../Magento/Reports/etc/adminhtml/menu.xml | 4 +- .../Magento/Reports/etc/adminhtml/routes.xml | 2 +- .../Magento/Reports/etc/adminhtml/system.xml | 2 +- app/code/Magento/Reports/etc/config.xml | 2 +- app/code/Magento/Reports/etc/di.xml | 2 +- .../Magento/Reports/etc/frontend/events.xml | 2 +- app/code/Magento/Reports/etc/module.xml | 2 +- .../Reports/etc/webapi_rest/events.xml | 2 +- .../Reports/etc/webapi_soap/events.xml | 2 +- app/code/Magento/Reports/etc/widget.xml | 2 +- app/code/Magento/Reports/registration.php | 2 +- .../reports_report_customer_accounts.xml | 2 +- .../reports_report_customer_accounts_grid.xml | 2 +- ...orts_report_customer_exportaccountscsv.xml | 2 +- ...ts_report_customer_exportaccountsexcel.xml | 2 +- ...eports_report_customer_exportorderscsv.xml | 2 +- ...orts_report_customer_exportordersexcel.xml | 2 +- ...eports_report_customer_exporttotalscsv.xml | 2 +- ...orts_report_customer_exporttotalsexcel.xml | 2 +- .../layout/reports_report_customer_orders.xml | 2 +- .../reports_report_customer_orders_grid.xml | 2 +- .../layout/reports_report_customer_totals.xml | 2 +- .../reports_report_customer_totals_grid.xml | 2 +- .../adminhtml/layout/reports_report_grid.xml | 2 +- .../reports_report_product_downloads.xml | 2 +- ...ports_report_product_exportlowstockcsv.xml | 2 +- ...rts_report_product_exportlowstockexcel.xml | 2 +- .../reports_report_product_exportsoldcsv.xml | 2 +- ...reports_report_product_exportsoldexcel.xml | 2 +- .../reports_report_product_lowstock.xml | 2 +- .../reports_report_product_lowstock_grid.xml | 2 +- .../layout/reports_report_product_sold.xml | 2 +- .../reports_report_product_sold_grid.xml | 2 +- .../layout/reports_report_product_viewed.xml | 2 +- .../layout/reports_report_review_customer.xml | 2 +- .../reports_report_review_customer_grid.xml | 2 +- ...eports_report_review_exportcustomercsv.xml | 2 +- ...orts_report_review_exportcustomerexcel.xml | 2 +- ...reports_report_review_exportproductcsv.xml | 2 +- ...ports_report_review_exportproductexcel.xml | 2 +- .../layout/reports_report_review_product.xml | 2 +- .../reports_report_review_product_grid.xml | 2 +- .../reports_report_sales_bestsellers.xml | 2 +- .../layout/reports_report_sales_coupons.xml | 2 +- .../layout/reports_report_sales_invoiced.xml | 2 +- .../layout/reports_report_sales_refunded.xml | 2 +- .../layout/reports_report_sales_sales.xml | 2 +- .../layout/reports_report_sales_shipping.xml | 2 +- .../layout/reports_report_sales_tax.xml | 2 +- .../reports_report_shopcart_abandoned.xml | 2 +- .../reports_report_statistics_index.xml | 2 +- .../view/adminhtml/layout/reports_sales.xml | 2 +- .../view/adminhtml/templates/grid.phtml | 2 +- .../templates/report/grid/container.phtml | 2 +- .../templates/report/refresh/statistics.phtml | 2 +- .../adminhtml/templates/report/wishlist.phtml | 2 +- .../adminhtml/templates/store/switcher.phtml | 2 +- .../templates/store/switcher/enhanced.phtml | 2 +- .../Reports/view/frontend/layout/default.xml | 2 +- .../Reports/view/frontend/layout/print.xml | 2 +- .../Reports/view/frontend/requirejs-config.js | 4 +- .../frontend/templates/js/components.phtml | 2 +- .../templates/product/widget/viewed.phtml | 2 +- .../product/widget/viewed/item.phtml | 2 +- .../column/compared_default_list.phtml | 2 +- .../column/compared_images_list.phtml | 2 +- .../compared/column/compared_names_list.phtml | 2 +- .../compared/content/compared_grid.phtml | 2 +- .../compared/content/compared_list.phtml | 2 +- .../viewed/column/viewed_default_list.phtml | 2 +- .../viewed/column/viewed_images_list.phtml | 2 +- .../viewed/column/viewed_names_list.phtml | 2 +- .../widget/viewed/content/viewed_grid.phtml | 2 +- .../widget/viewed/content/viewed_list.phtml | 2 +- .../view/frontend/web/js/recently-viewed.js | 57 +- .../RequireJs/Block/Html/Head/Config.php | 2 +- .../Magento/RequireJs/Model/FileManager.php | 2 +- .../Test/Unit/Block/Html/Head/ConfigTest.php | 2 +- .../Test/Unit/Model/FileManagerTest.php | 2 +- app/code/Magento/RequireJs/composer.json | 2 +- app/code/Magento/RequireJs/etc/di.xml | 2 +- app/code/Magento/RequireJs/etc/module.xml | 2 +- app/code/Magento/RequireJs/registration.php | 2 +- .../Magento/Review/Block/Adminhtml/Add.php | 2 +- .../Review/Block/Adminhtml/Add/Form.php | 2 +- .../Magento/Review/Block/Adminhtml/Edit.php | 2 +- .../Review/Block/Adminhtml/Edit/Form.php | 2 +- .../Magento/Review/Block/Adminhtml/Grid.php | 2 +- .../Block/Adminhtml/Grid/Filter/Type.php | 2 +- .../Block/Adminhtml/Grid/Renderer/Type.php | 2 +- .../Magento/Review/Block/Adminhtml/Main.php | 2 +- .../Block/Adminhtml/Product/Edit/Tab.php | 2 +- .../Adminhtml/Product/Edit/Tab/Reviews.php | 2 +- .../Review/Block/Adminhtml/Product/Grid.php | 2 +- .../Magento/Review/Block/Adminhtml/Rating.php | 2 +- .../Block/Adminhtml/Rating/Detailed.php | 2 +- .../Review/Block/Adminhtml/Rating/Edit.php | 2 +- .../Block/Adminhtml/Rating/Edit/Form.php | 2 +- .../Block/Adminhtml/Rating/Edit/Tab/Form.php | 2 +- .../Block/Adminhtml/Rating/Edit/Tabs.php | 2 +- .../Review/Block/Adminhtml/Rating/Summary.php | 2 +- .../Review/Block/Adminhtml/ReviewTab.php | 2 +- .../Magento/Review/Block/Adminhtml/Rss.php | 2 +- .../Review/Block/Adminhtml/Rss/Grid/Link.php | 2 +- .../Review/Block/Customer/ListCustomer.php | 2 +- .../Magento/Review/Block/Customer/Recent.php | 2 +- .../Magento/Review/Block/Customer/View.php | 2 +- app/code/Magento/Review/Block/Form.php | 2 +- .../Magento/Review/Block/Form/Configure.php | 2 +- .../Compare/ListCompare/Plugin/Review.php | 2 +- .../Magento/Review/Block/Product/Review.php | 2 +- .../Review/Block/Product/ReviewRenderer.php | 2 +- .../Magento/Review/Block/Product/View.php | 2 +- .../Review/Block/Product/View/ListView.php | 2 +- .../Review/Block/Product/View/Other.php | 2 +- .../Review/Block/Rating/Entity/Detailed.php | 2 +- app/code/Magento/Review/Block/View.php | 2 +- .../Review/Controller/Adminhtml/Product.php | 2 +- .../Controller/Adminhtml/Product/Delete.php | 2 +- .../Controller/Adminhtml/Product/Edit.php | 2 +- .../Controller/Adminhtml/Product/Index.php | 2 +- .../Adminhtml/Product/JsonProductInfo.php | 2 +- .../Adminhtml/Product/MassDelete.php | 2 +- .../Adminhtml/Product/MassUpdateStatus.php | 2 +- .../Adminhtml/Product/MassVisibleIn.php | 2 +- .../Adminhtml/Product/NewAction.php | 2 +- .../Controller/Adminhtml/Product/Pending.php | 2 +- .../Controller/Adminhtml/Product/Post.php | 2 +- .../Adminhtml/Product/ProductGrid.php | 2 +- .../Adminhtml/Product/RatingItems.php | 2 +- .../Adminhtml/Product/ReviewGrid.php | 2 +- .../Adminhtml/Product/Reviews/Grid.php | 2 +- .../Controller/Adminhtml/Product/Save.php | 2 +- .../Review/Controller/Adminhtml/Rating.php | 2 +- .../Controller/Adminhtml/Rating/Delete.php | 2 +- .../Controller/Adminhtml/Rating/Edit.php | 2 +- .../Controller/Adminhtml/Rating/Index.php | 2 +- .../Controller/Adminhtml/Rating/NewAction.php | 2 +- .../Controller/Adminhtml/Rating/Save.php | 2 +- .../Magento/Review/Controller/Customer.php | 2 +- .../Review/Controller/Customer/Index.php | 2 +- .../Review/Controller/Customer/View.php | 2 +- .../Magento/Review/Controller/Product.php | 2 +- .../Review/Controller/Product/ListAction.php | 7 +- .../Review/Controller/Product/ListAjax.php | 2 +- .../Review/Controller/Product/Post.php | 2 +- .../Review/Controller/Product/View.php | 2 +- .../Magento/Review/CustomerData/Review.php | 2 +- .../Magento/Review/Helper/Action/Pager.php | 2 +- app/code/Magento/Review/Helper/Data.php | 2 +- app/code/Magento/Review/Model/Rating.php | 2 +- .../Magento/Review/Model/Rating/Entity.php | 2 +- .../Magento/Review/Model/Rating/Option.php | 2 +- .../Review/Model/Rating/Option/Vote.php | 2 +- .../Review/Model/ResourceModel/Rating.php | 2 +- .../Model/ResourceModel/Rating/Collection.php | 2 +- .../Model/ResourceModel/Rating/Entity.php | 2 +- .../ResourceModel/Rating/Grid/Collection.php | 2 +- .../Model/ResourceModel/Rating/Option.php | 2 +- .../Rating/Option/Collection.php | 2 +- .../ResourceModel/Rating/Option/Vote.php | 2 +- .../Rating/Option/Vote/Collection.php | 2 +- .../Review/Model/ResourceModel/Review.php | 2 +- .../Model/ResourceModel/Review/Collection.php | 2 +- .../Review/Product/Collection.php | 19 +- .../Model/ResourceModel/Review/Status.php | 2 +- .../Review/Status/Collection.php | 2 +- .../Model/ResourceModel/Review/Summary.php | 2 +- .../Review/Summary/Collection.php | 2 +- app/code/Magento/Review/Model/Review.php | 2 +- .../Magento/Review/Model/Review/Status.php | 2 +- .../Magento/Review/Model/Review/Summary.php | 2 +- app/code/Magento/Review/Model/Rss.php | 2 +- ...kProductCollectionBeforeToHtmlObserver.php | 2 +- ...ProcessProductAfterDeleteEventObserver.php | 2 +- .../TagProductCollectionLoadAfterObserver.php | 2 +- app/code/Magento/Review/Setup/InstallData.php | 2 +- .../Magento/Review/Setup/InstallSchema.php | 2 +- .../Test/Unit/Block/Adminhtml/MainTest.php | 2 +- .../Adminhtml/Rating/Edit/Tab/FormTest.php | 2 +- .../Block/Adminhtml/Rss/Grid/LinkTest.php | 2 +- .../Test/Unit/Block/Adminhtml/RssTest.php | 2 +- .../Test/Unit/Block/Customer/RecentTest.php | 2 +- .../Review/Test/Unit/Block/FormTest.php | 2 +- .../Test/Unit/Block/Product/ReviewTest.php | 2 +- .../Controller/Adminhtml/Product/PostTest.php | 2 +- .../Test/Unit/Controller/Product/PostTest.php | 2 +- .../Test/Unit/Helper/Action/PagerTest.php | 2 +- .../Review/Test/Unit/Model/RatingTest.php | 2 +- .../ResourceModel/Review/CollectionTest.php | 2 +- .../Review/Product/CollectionTest.php | 36 +- .../Review/Summary/CollectionTest.php | 2 +- .../Review/Test/Unit/Model/ReviewTest.php | 2 +- .../Review/Test/Unit/Model/RssTest.php | 2 +- .../Listing/Columns/ReviewActionsTest.php | 2 +- .../Component/Listing/Columns/StatusTest.php | 2 +- .../Ui/Component/Listing/Columns/TypeTest.php | 2 +- .../Listing/Columns/VisibilityTest.php | 2 +- .../Product/Form/Modifier/ReviewTest.php | 49 +- .../Product/ReviewDataProviderTest.php | 2 +- .../Listing/Columns/ReviewActions.php | 2 +- .../Ui/Component/Listing/Columns/Status.php | 2 +- .../Ui/Component/Listing/Columns/Type.php | 2 +- .../Component/Listing/Columns/Visibility.php | 2 +- .../Product/Form/Modifier/Review.php | 26 +- .../Product/ReviewDataProvider.php | 2 +- app/code/Magento/Review/composer.json | 2 +- app/code/Magento/Review/etc/acl.xml | 2 +- app/code/Magento/Review/etc/adminhtml/di.xml | 2 +- .../Magento/Review/etc/adminhtml/events.xml | 2 +- .../Magento/Review/etc/adminhtml/menu.xml | 2 +- .../Magento/Review/etc/adminhtml/routes.xml | 2 +- .../Magento/Review/etc/adminhtml/system.xml | 2 +- app/code/Magento/Review/etc/config.xml | 2 +- app/code/Magento/Review/etc/di.xml | 7 +- app/code/Magento/Review/etc/frontend/di.xml | 2 +- .../Magento/Review/etc/frontend/events.xml | 2 +- .../Review/etc/frontend/page_types.xml | 2 +- .../Magento/Review/etc/frontend/routes.xml | 4 +- .../Magento/Review/etc/frontend/sections.xml | 2 +- app/code/Magento/Review/etc/module.xml | 2 +- app/code/Magento/Review/registration.php | 2 +- .../adminhtml/layout/catalog_product_new.xml | 2 +- .../adminhtml/layout/customer_index_edit.xml | 2 +- .../view/adminhtml/layout/rating_block.xml | 2 +- .../adminhtml/layout/review_product_edit.xml | 2 +- .../adminhtml/layout/review_product_index.xml | 2 +- .../layout/review_product_reviews_grid.xml | 2 +- .../adminhtml/layout/review_rating_edit.xml | 2 +- .../adminhtml/layout/review_rating_index.xml | 2 +- .../Review/view/adminhtml/templates/add.phtml | 2 +- .../adminhtml/templates/rating/detailed.phtml | 2 +- .../adminhtml/templates/rating/form.phtml | 2 +- .../adminhtml/templates/rating/options.phtml | 2 +- .../templates/rating/stars/detailed.phtml | 2 +- .../templates/rating/stars/summary.phtml | 2 +- .../adminhtml/templates/rss/grid/link.phtml | 2 +- .../adminhtml/ui_component/review_listing.xml | 2 +- .../Review/view/adminhtml/web/js/rating.js | 43 +- .../frontend/layout/catalog_product_view.xml | 2 +- .../layout/checkout_cart_configure.xml | 2 +- .../view/frontend/layout/customer_account.xml | 5 +- .../layout/customer_account_index.xml | 2 +- .../frontend/layout/review_customer_index.xml | 2 +- .../frontend/layout/review_customer_view.xml | 2 +- .../layout/review_product_form_component.xml | 4 +- .../frontend/layout/review_product_list.xml | 2 +- .../layout/review_product_listajax.xml | 2 +- .../frontend/layout/review_product_view.xml | 2 +- .../layout/wishlist_index_configure.xml | 2 +- .../frontend/templates/customer/list.phtml | 2 +- .../frontend/templates/customer/recent.phtml | 2 +- .../frontend/templates/customer/view.phtml | 2 +- .../view/frontend/templates/detailed.phtml | 2 +- .../view/frontend/templates/empty.phtml | 2 +- .../Review/view/frontend/templates/form.phtml | 4 +- .../frontend/templates/helper/summary.phtml | 2 +- .../templates/helper/summary_short.phtml | 2 +- .../templates/product/view/count.phtml | 2 +- .../templates/product/view/list.phtml | 2 +- .../templates/product/view/other.phtml | 2 +- .../view/frontend/templates/redirect.phtml | 2 +- .../view/frontend/templates/review.phtml | 5 +- .../Review/view/frontend/templates/view.phtml | 2 +- .../view/frontend/web/js/error-placement.js | 12 +- .../view/frontend/web/js/process-reviews.js | 37 +- .../view/frontend/web/js/view/review.js | 16 +- .../Action/Plugin/BackendAuthentication.php | 2 +- app/code/Magento/Rss/Block/Feeds.php | 2 +- .../Magento/Rss/Controller/Adminhtml/Feed.php | 2 +- .../Rss/Controller/Adminhtml/Feed/Index.php | 2 +- app/code/Magento/Rss/Controller/Feed.php | 2 +- .../Magento/Rss/Controller/Feed/Index.php | 2 +- app/code/Magento/Rss/Controller/Index.php | 2 +- .../Magento/Rss/Controller/Index/Index.php | 2 +- app/code/Magento/Rss/Model/Rss.php | 28 +- app/code/Magento/Rss/Model/RssManager.php | 2 +- .../Rss/Model/System/Config/Backend/Links.php | 2 +- app/code/Magento/Rss/Model/UrlBuilder.php | 2 +- .../Plugin/BackendAuthenticationTest.php | 2 +- .../Magento/Rss/Test/Unit/Block/FeedsTest.php | 2 +- .../Controller/Adminhtml/Feed/IndexTest.php | 2 +- .../Test/Unit/Controller/Feed/IndexTest.php | 2 +- .../Rss/Test/Unit/Model/RssManagerTest.php | 2 +- .../Magento/Rss/Test/Unit/Model/RssTest.php | 43 +- .../Rss/Test/Unit/Model/UrlBuilderTest.php | 2 +- app/code/Magento/Rss/composer.json | 2 +- app/code/Magento/Rss/etc/acl.xml | 2 +- app/code/Magento/Rss/etc/adminhtml/di.xml | 2 +- app/code/Magento/Rss/etc/adminhtml/routes.xml | 2 +- app/code/Magento/Rss/etc/adminhtml/system.xml | 2 +- app/code/Magento/Rss/etc/di.xml | 2 +- .../Magento/Rss/etc/frontend/page_types.xml | 2 +- app/code/Magento/Rss/etc/frontend/routes.xml | 2 +- app/code/Magento/Rss/etc/module.xml | 2 +- app/code/Magento/Rss/registration.php | 2 +- .../Rss/view/frontend/layout/default.xml | 2 +- .../view/frontend/layout/rss_index_index.xml | 2 +- .../Rss/view/frontend/templates/feeds.phtml | 2 +- app/code/Magento/Rule/Block/Actions.php | 2 +- app/code/Magento/Rule/Block/Conditions.php | 2 +- app/code/Magento/Rule/Block/Editable.php | 2 +- app/code/Magento/Rule/Block/Newchild.php | 2 +- app/code/Magento/Rule/Block/Rule.php | 2 +- app/code/Magento/Rule/Model/AbstractModel.php | 37 +- .../Rule/Model/Action/AbstractAction.php | 2 +- .../Rule/Model/Action/ActionInterface.php | 2 +- .../Magento/Rule/Model/Action/Collection.php | 2 +- app/code/Magento/Rule/Model/ActionFactory.php | 2 +- .../Model/Condition/AbstractCondition.php | 2 +- .../Magento/Rule/Model/Condition/Combine.php | 2 +- .../Model/Condition/ConditionInterface.php | 2 +- .../Magento/Rule/Model/Condition/Context.php | 2 +- .../Condition/Product/AbstractProduct.php | 6 +- .../Rule/Model/Condition/Sql/Builder.php | 2 +- .../Rule/Model/Condition/Sql/Expression.php | 2 +- .../Magento/Rule/Model/ConditionFactory.php | 2 +- .../Magento/Rule/Model/Renderer/Actions.php | 2 +- .../Rule/Model/Renderer/Conditions.php | 2 +- .../Model/ResourceModel/AbstractResource.php | 2 +- .../Rule/Collection/AbstractCollection.php | 2 +- .../Test/Unit/Model/AbstractModelTest.php | 209 + .../Test/Unit/Model/ActionFactoryTest.php | 2 +- .../Model/Condition/AbstractConditionTest.php | 2 +- .../Test/Unit/Model/Condition/CombineTest.php | 2 +- .../Condition/Product/AbstractProductTest.php | 2 +- .../Unit/Model/Condition/Sql/BuilderTest.php | 2 +- .../Model/Condition/Sql/ExpressionTest.php | 2 +- .../Test/Unit/Model/ConditionFactoryTest.php | 2 +- .../Test/Unit/Model/Renderer/ActionsTest.php | 2 +- .../Unit/Model/Renderer/ConditionsTest.php | 2 +- .../Collection/AbstractCollectionTest.php | 2 +- app/code/Magento/Rule/composer.json | 2 +- app/code/Magento/Rule/etc/module.xml | 2 +- app/code/Magento/Rule/registration.php | 2 +- .../Magento/Rule/view/adminhtml/web/rules.js | 5 +- .../CreditmemoCommentRepositoryInterface.php | 2 +- .../Api/CreditmemoItemRepositoryInterface.php | 2 +- .../Api/CreditmemoManagementInterface.php | 2 +- .../Api/CreditmemoRepositoryInterface.php | 2 +- .../Sales/Api/Data/CommentInterface.php | 2 +- .../CreditmemoCommentCreationInterface.php | 2 +- .../Api/Data/CreditmemoCommentInterface.php | 2 +- ...CreditmemoCommentSearchResultInterface.php | 2 +- .../CreditmemoCreationArgumentsInterface.php | 2 +- .../Sales/Api/Data/CreditmemoInterface.php | 2 +- .../Data/CreditmemoItemCreationInterface.php | 2 +- .../Api/Data/CreditmemoItemInterface.php | 2 +- .../CreditmemoItemSearchResultInterface.php | 2 +- .../Data/CreditmemoSearchResultInterface.php | 2 +- .../Sales/Api/Data/EntityInterface.php | 2 +- .../Data/InvoiceCommentCreationInterface.php | 2 +- .../Api/Data/InvoiceCommentInterface.php | 2 +- .../InvoiceCommentSearchResultInterface.php | 2 +- .../InvoiceCreationArgumentsInterface.php | 2 +- .../Sales/Api/Data/InvoiceInterface.php | 2 +- .../Api/Data/InvoiceItemCreationInterface.php | 2 +- .../Sales/Api/Data/InvoiceItemInterface.php | 2 +- .../Data/InvoiceItemSearchResultInterface.php | 2 +- .../Api/Data/InvoiceSearchResultInterface.php | 2 +- .../Sales/Api/Data/LineItemInterface.php | 2 +- .../Sales/Api/Data/OrderAddressInterface.php | 2 +- .../OrderAddressSearchResultInterface.php | 2 +- .../Magento/Sales/Api/Data/OrderInterface.php | 2 +- .../Sales/Api/Data/OrderItemInterface.php | 2 +- .../Data/OrderItemSearchResultInterface.php | 2 +- .../Sales/Api/Data/OrderPaymentInterface.php | 2 +- .../OrderPaymentSearchResultInterface.php | 2 +- .../Api/Data/OrderSearchResultInterface.php | 2 +- .../Api/Data/OrderStatusHistoryInterface.php | 2 +- ...rderStatusHistorySearchResultInterface.php | 2 +- .../Data/ShipmentCommentCreationInterface.php | 2 +- .../Api/Data/ShipmentCommentInterface.php | 2 +- .../ShipmentCommentSearchResultInterface.php | 2 +- .../ShipmentCreationArgumentsInterface.php | 2 +- .../Sales/Api/Data/ShipmentInterface.php | 2 +- .../Data/ShipmentItemCreationInterface.php | 2 +- .../Sales/Api/Data/ShipmentItemInterface.php | 2 +- .../ShipmentItemSearchResultInterface.php | 2 +- .../Data/ShipmentPackageCreationInterface.php | 2 +- .../Api/Data/ShipmentPackageInterface.php | 2 +- .../Data/ShipmentSearchResultInterface.php | 2 +- .../Data/ShipmentTrackCreationInterface.php | 2 +- .../Sales/Api/Data/ShipmentTrackInterface.php | 2 +- .../ShipmentTrackSearchResultInterface.php | 2 +- .../Api/Data/ShippingAssignmentInterface.php | 2 +- .../Sales/Api/Data/ShippingInterface.php | 2 +- .../Magento/Sales/Api/Data/TotalInterface.php | 2 +- .../Magento/Sales/Api/Data/TrackInterface.php | 2 +- .../Sales/Api/Data/TransactionInterface.php | 2 +- .../Data/TransactionSearchResultInterface.php | 2 +- .../CouldNotInvoiceExceptionInterface.php | 2 +- .../CouldNotRefundExceptionInterface.php | 2 +- .../CouldNotShipExceptionInterface.php | 2 +- .../DocumentValidationExceptionInterface.php | 2 +- .../Api/InvoiceCommentRepositoryInterface.php | 2 +- .../Api/InvoiceItemRepositoryInterface.php | 2 +- .../Sales/Api/InvoiceManagementInterface.php | 2 +- .../Sales/Api/InvoiceOrderInterface.php | 2 +- .../Sales/Api/InvoiceRepositoryInterface.php | 2 +- .../Api/OrderAddressRepositoryInterface.php | 2 +- .../Api/OrderCustomerManagementInterface.php | 2 +- .../Api/OrderItemRepositoryInterface.php | 2 +- .../Sales/Api/OrderManagementInterface.php | 2 +- .../Api/OrderPaymentRepositoryInterface.php | 2 +- .../Sales/Api/OrderRepositoryInterface.php | 2 +- .../OrderStatusHistoryRepositoryInterface.php | 2 +- .../Sales/Api/RefundInvoiceInterface.php | 2 +- .../Sales/Api/RefundOrderInterface.php | 2 +- .../Magento/Sales/Api/ShipOrderInterface.php | 2 +- .../ShipmentCommentRepositoryInterface.php | 2 +- .../Api/ShipmentItemRepositoryInterface.php | 2 +- .../Sales/Api/ShipmentManagementInterface.php | 2 +- .../Sales/Api/ShipmentRepositoryInterface.php | 2 +- .../Api/ShipmentTrackRepositoryInterface.php | 2 +- .../Api/TransactionRepositoryInterface.php | 2 +- .../Sales/Block/Adminhtml/Creditmemo.php | 2 +- .../Block/Adminhtml/CustomerOrdersTab.php | 2 +- .../Magento/Sales/Block/Adminhtml/Invoice.php | 2 +- .../Block/Adminhtml/Items/AbstractItems.php | 2 +- .../Adminhtml/Items/Column/DefaultColumn.php | 2 +- .../Block/Adminhtml/Items/Column/Name.php | 2 +- .../Block/Adminhtml/Items/Column/Qty.php | 2 +- .../Items/Renderer/DefaultRenderer.php | 2 +- .../Magento/Sales/Block/Adminhtml/Order.php | 2 +- .../Block/Adminhtml/Order/AbstractOrder.php | 2 +- .../Sales/Block/Adminhtml/Order/Address.php | 2 +- .../Block/Adminhtml/Order/Address/Form.php | 2 +- .../Block/Adminhtml/Order/Comments/View.php | 2 +- .../Sales/Block/Adminhtml/Order/Create.php | 2 +- .../Adminhtml/Order/Create/AbstractCreate.php | 2 +- .../Order/Create/Billing/Address.php | 2 +- .../Adminhtml/Order/Create/Billing/Method.php | 2 +- .../Order/Create/Billing/Method/Form.php | 2 +- .../Block/Adminhtml/Order/Create/Comment.php | 2 +- .../Block/Adminhtml/Order/Create/Coupons.php | 2 +- .../Adminhtml/Order/Create/Coupons/Form.php | 2 +- .../Block/Adminhtml/Order/Create/Customer.php | 2 +- .../Block/Adminhtml/Order/Create/Data.php | 2 +- .../Block/Adminhtml/Order/Create/Form.php | 2 +- .../Order/Create/Form/AbstractForm.php | 2 +- .../Adminhtml/Order/Create/Form/Account.php | 2 +- .../Adminhtml/Order/Create/Form/Address.php | 2 +- .../Adminhtml/Order/Create/Giftmessage.php | 2 +- .../Order/Create/Giftmessage/Form.php | 2 +- .../Block/Adminhtml/Order/Create/Header.php | 2 +- .../Block/Adminhtml/Order/Create/Items.php | 2 +- .../Adminhtml/Order/Create/Items/Grid.php | 4 +- .../Block/Adminhtml/Order/Create/Load.php | 2 +- .../Block/Adminhtml/Order/Create/Messages.php | 2 +- .../Adminhtml/Order/Create/Newsletter.php | 2 +- .../Order/Create/Newsletter/Form.php | 2 +- .../Block/Adminhtml/Order/Create/Search.php | 2 +- .../Adminhtml/Order/Create/Search/Grid.php | 2 +- .../Create/Search/Grid/Renderer/Price.php | 2 +- .../Create/Search/Grid/Renderer/Product.php | 2 +- .../Order/Create/Search/Grid/Renderer/Qty.php | 2 +- .../Order/Create/Shipping/Address.php | 2 +- .../Order/Create/Shipping/Method.php | 2 +- .../Order/Create/Shipping/Method/Form.php | 2 +- .../Block/Adminhtml/Order/Create/Sidebar.php | 2 +- .../Order/Create/Sidebar/AbstractSidebar.php | 2 +- .../Adminhtml/Order/Create/Sidebar/Cart.php | 2 +- .../Order/Create/Sidebar/Compared.php | 2 +- .../Order/Create/Sidebar/Pcompared.php | 2 +- .../Order/Create/Sidebar/Pviewed.php | 2 +- .../Order/Create/Sidebar/Reorder.php | 2 +- .../Adminhtml/Order/Create/Sidebar/Viewed.php | 2 +- .../Order/Create/Sidebar/Wishlist.php | 2 +- .../Block/Adminhtml/Order/Create/Store.php | 2 +- .../Adminhtml/Order/Create/Store/Select.php | 2 +- .../Block/Adminhtml/Order/Create/Totals.php | 2 +- .../Order/Create/Totals/DefaultTotals.php | 2 +- .../Order/Create/Totals/Discount.php | 2 +- .../Order/Create/Totals/Grandtotal.php | 2 +- .../Order/Create/Totals/Shipping.php | 2 +- .../Order/Create/Totals/Subtotal.php | 2 +- .../Adminhtml/Order/Create/Totals/Table.php | 2 +- .../Adminhtml/Order/Create/Totals/Tax.php | 2 +- .../Adminhtml/Order/Creditmemo/Create.php | 2 +- .../Order/Creditmemo/Create/Adjustments.php | 2 +- .../Order/Creditmemo/Create/Form.php | 2 +- .../Order/Creditmemo/Create/Items.php | 2 +- .../Adminhtml/Order/Creditmemo/Totals.php | 2 +- .../Block/Adminhtml/Order/Creditmemo/View.php | 2 +- .../Order/Creditmemo/View/Comments.php | 2 +- .../Adminhtml/Order/Creditmemo/View/Form.php | 2 +- .../Adminhtml/Order/Creditmemo/View/Items.php | 2 +- .../Sales/Block/Adminhtml/Order/Details.php | 2 +- .../Block/Adminhtml/Order/Invoice/Create.php | 2 +- .../Adminhtml/Order/Invoice/Create/Form.php | 2 +- .../Adminhtml/Order/Invoice/Create/Items.php | 2 +- .../Block/Adminhtml/Order/Invoice/Totals.php | 2 +- .../Block/Adminhtml/Order/Invoice/View.php | 2 +- .../Adminhtml/Order/Invoice/View/Comments.php | 2 +- .../Adminhtml/Order/Invoice/View/Form.php | 2 +- .../Adminhtml/Order/Invoice/View/Items.php | 2 +- .../Sales/Block/Adminhtml/Order/Payment.php | 2 +- .../Sales/Block/Adminhtml/Order/Status.php | 2 +- .../Block/Adminhtml/Order/Status/Assign.php | 2 +- .../Adminhtml/Order/Status/Assign/Form.php | 2 +- .../Block/Adminhtml/Order/Status/Edit.php | 2 +- .../Adminhtml/Order/Status/Edit/Form.php | 2 +- .../Adminhtml/Order/Status/NewStatus.php | 2 +- .../Adminhtml/Order/Status/NewStatus/Form.php | 2 +- .../Sales/Block/Adminhtml/Order/Totalbar.php | 2 +- .../Sales/Block/Adminhtml/Order/Totals.php | 2 +- .../Block/Adminhtml/Order/Totals/Item.php | 2 +- .../Block/Adminhtml/Order/Totals/Tax.php | 2 +- .../Sales/Block/Adminhtml/Order/View.php | 2 +- .../Sales/Block/Adminhtml/Order/View/Form.php | 2 +- .../Adminhtml/Order/View/Giftmessage.php | 2 +- .../Block/Adminhtml/Order/View/History.php | 2 +- .../Sales/Block/Adminhtml/Order/View/Info.php | 2 +- .../Block/Adminhtml/Order/View/Items.php | 2 +- .../View/Items/Renderer/DefaultRenderer.php | 2 +- .../Block/Adminhtml/Order/View/Messages.php | 2 +- .../Adminhtml/Order/View/Tab/Creditmemos.php | 2 +- .../Adminhtml/Order/View/Tab/History.php | 9 +- .../Block/Adminhtml/Order/View/Tab/Info.php | 2 +- .../Adminhtml/Order/View/Tab/Invoices.php | 2 +- .../Adminhtml/Order/View/Tab/Shipments.php | 2 +- .../Adminhtml/Order/View/Tab/Transactions.php | 2 +- .../Sales/Block/Adminhtml/Order/View/Tabs.php | 2 +- .../Adminhtml/Reorder/Renderer/Action.php | 2 +- .../Block/Adminhtml/Report/Filter/Form.php | 2 +- .../Adminhtml/Report/Filter/Form/Coupon.php | 2 +- .../Adminhtml/Report/Filter/Form/Order.php | 2 +- .../Block/Adminhtml/Rss/Order/Grid/Link.php | 2 +- .../Sales/Block/Adminhtml/Shipment.php | 2 +- .../Config/Form/Fieldset/Order/Statuses.php | 2 +- .../Magento/Sales/Block/Adminhtml/Totals.php | 2 +- .../Sales/Block/Adminhtml/Transactions.php | 2 +- .../Block/Adminhtml/Transactions/Detail.php | 4 +- .../Adminhtml/Transactions/Detail/Grid.php | 2 +- app/code/Magento/Sales/Block/Guest/Link.php | 2 +- .../Sales/Block/Items/AbstractItems.php | 2 +- .../Magento/Sales/Block/Order/Comments.php | 2 +- .../Magento/Sales/Block/Order/Creditmemo.php | 2 +- .../Sales/Block/Order/Creditmemo/Items.php | 2 +- .../Sales/Block/Order/Creditmemo/Totals.php | 2 +- .../Block/Order/Email/Creditmemo/Items.php | 2 +- .../Sales/Block/Order/Email/Invoice/Items.php | 2 +- .../Magento/Sales/Block/Order/Email/Items.php | 2 +- .../Block/Order/Email/Items/DefaultItems.php | 2 +- .../Order/Email/Items/Order/DefaultOrder.php | 2 +- .../Block/Order/Email/Shipment/Items.php | 2 +- .../Magento/Sales/Block/Order/History.php | 2 +- .../Sales/Block/Order/History/Container.php | 2 +- app/code/Magento/Sales/Block/Order/Info.php | 2 +- .../Sales/Block/Order/Info/Buttons.php | 2 +- .../Sales/Block/Order/Info/Buttons/Rss.php | 2 +- .../Magento/Sales/Block/Order/Invoice.php | 2 +- .../Sales/Block/Order/Invoice/Items.php | 2 +- .../Sales/Block/Order/Invoice/Totals.php | 2 +- .../Order/Item/Renderer/DefaultRenderer.php | 2 +- app/code/Magento/Sales/Block/Order/Items.php | 87 +- app/code/Magento/Sales/Block/Order/Link.php | 2 +- .../Block/Order/PrintOrder/Creditmemo.php | 2 +- .../Sales/Block/Order/PrintOrder/Invoice.php | 2 +- .../Sales/Block/Order/PrintOrder/Shipment.php | 2 +- .../Sales/Block/Order/PrintShipment.php | 27 +- app/code/Magento/Sales/Block/Order/Recent.php | 2 +- app/code/Magento/Sales/Block/Order/Totals.php | 2 +- app/code/Magento/Sales/Block/Order/View.php | 2 +- .../Magento/Sales/Block/Reorder/Sidebar.php | 2 +- .../Sales/Block/Status/Grid/Column/State.php | 5 +- .../Block/Status/Grid/Column/Unassign.php | 2 +- .../Magento/Sales/Block/Widget/Guest/Form.php | 2 +- .../AbstractController/Creditmemo.php | 2 +- .../Controller/AbstractController/Invoice.php | 2 +- .../AbstractController/OrderLoader.php | 2 +- .../OrderLoaderInterface.php | 2 +- .../OrderViewAuthorization.php | 2 +- .../OrderViewAuthorizationInterface.php | 2 +- .../AbstractController/PrintAction.php | 2 +- .../AbstractController/PrintCreditmemo.php | 2 +- .../AbstractController/PrintInvoice.php | 2 +- .../AbstractController/PrintShipment.php | 2 +- .../Controller/AbstractController/Reorder.php | 2 +- .../AbstractController/Shipment.php | 2 +- .../Controller/AbstractController/View.php | 2 +- .../Creditmemo/AbstractCreditmemo/Email.php | 2 +- .../Creditmemo/AbstractCreditmemo/Grid.php | 2 +- .../Creditmemo/AbstractCreditmemo/Index.php | 2 +- .../AbstractCreditmemo/Pdfcreditmemos.php | 2 +- .../AbstractCreditmemo/PrintAction.php | 2 +- .../Creditmemo/AbstractCreditmemo/View.php | 2 +- .../Controller/Adminhtml/Creditmemo/Email.php | 2 +- .../Controller/Adminhtml/Creditmemo/Grid.php | 2 +- .../Controller/Adminhtml/Creditmemo/Index.php | 2 +- .../Adminhtml/Creditmemo/Pdfcreditmemos.php | 2 +- .../Adminhtml/Creditmemo/PrintAction.php | 2 +- .../Controller/Adminhtml/Creditmemo/View.php | 2 +- .../Invoice/AbstractInvoice/Email.php | 2 +- .../Invoice/AbstractInvoice/Grid.php | 2 +- .../Invoice/AbstractInvoice/Index.php | 2 +- .../Invoice/AbstractInvoice/Pdfinvoices.php | 2 +- .../Invoice/AbstractInvoice/PrintAction.php | 2 +- .../Invoice/AbstractInvoice/View.php | 2 +- .../Controller/Adminhtml/Invoice/Email.php | 2 +- .../Controller/Adminhtml/Invoice/Grid.php | 2 +- .../Controller/Adminhtml/Invoice/Index.php | 2 +- .../Adminhtml/Invoice/Pdfinvoices.php | 2 +- .../Adminhtml/Invoice/PrintAction.php | 2 +- .../Controller/Adminhtml/Invoice/View.php | 2 +- .../Sales/Controller/Adminhtml/Order.php | 2 +- .../Adminhtml/Order/AbstractMassAction.php | 2 +- .../Controller/Adminhtml/Order/AddComment.php | 2 +- .../Controller/Adminhtml/Order/Address.php | 2 +- .../Adminhtml/Order/AddressSave.php | 2 +- .../Controller/Adminhtml/Order/Cancel.php | 2 +- .../Adminhtml/Order/CommentsHistory.php | 2 +- .../Controller/Adminhtml/Order/Create.php | 2 +- .../Adminhtml/Order/Create/AddConfigured.php | 2 +- .../Adminhtml/Order/Create/Cancel.php | 2 +- .../Order/Create/ConfigureProductToAdd.php | 2 +- .../Order/Create/ConfigureQuoteItems.php | 2 +- .../Adminhtml/Order/Create/Index.php | 2 +- .../Adminhtml/Order/Create/LoadBlock.php | 2 +- .../Adminhtml/Order/Create/ProcessData.php | 2 +- .../Adminhtml/Order/Create/Reorder.php | 2 +- .../Adminhtml/Order/Create/Save.php | 2 +- .../Order/Create/ShowUpdateResult.php | 2 +- .../Adminhtml/Order/Create/Start.php | 2 +- .../Adminhtml/Order/Creditmemo/AddComment.php | 2 +- .../Adminhtml/Order/Creditmemo/Cancel.php | 2 +- .../Adminhtml/Order/Creditmemo/Email.php | 2 +- .../Adminhtml/Order/Creditmemo/Grid.php | 2 +- .../Adminhtml/Order/Creditmemo/Index.php | 2 +- .../Adminhtml/Order/Creditmemo/NewAction.php | 2 +- .../Order/Creditmemo/Pdfcreditmemos.php | 2 +- .../Order/Creditmemo/PrintAction.php | 2 +- .../Adminhtml/Order/Creditmemo/Save.php | 3 +- .../Adminhtml/Order/Creditmemo/Start.php | 2 +- .../Adminhtml/Order/Creditmemo/UpdateQty.php | 2 +- .../Adminhtml/Order/Creditmemo/View.php | 2 +- .../Creditmemo/{Void.php => VoidAction.php} | 4 +- .../Adminhtml/Order/CreditmemoLoader.php | 6 +- .../Adminhtml/Order/Creditmemos.php | 2 +- .../Adminhtml/Order/Edit/AddConfigured.php | 2 +- .../Adminhtml/Order/Edit/Cancel.php | 2 +- .../Order/Edit/ConfigureProductToAdd.php | 2 +- .../Order/Edit/ConfigureQuoteItems.php | 2 +- .../Controller/Adminhtml/Order/Edit/Index.php | 2 +- .../Adminhtml/Order/Edit/LoadBlock.php | 2 +- .../Adminhtml/Order/Edit/ProcessData.php | 2 +- .../Adminhtml/Order/Edit/Reorder.php | 2 +- .../Controller/Adminhtml/Order/Edit/Save.php | 2 +- .../Adminhtml/Order/Edit/ShowUpdateResult.php | 2 +- .../Controller/Adminhtml/Order/Edit/Start.php | 2 +- .../Controller/Adminhtml/Order/Email.php | 2 +- .../Sales/Controller/Adminhtml/Order/Grid.php | 2 +- .../Sales/Controller/Adminhtml/Order/Hold.php | 2 +- .../Controller/Adminhtml/Order/Index.php | 2 +- .../Adminhtml/Order/Invoice/AddComment.php | 2 +- .../Adminhtml/Order/Invoice/Cancel.php | 2 +- .../Adminhtml/Order/Invoice/Capture.php | 2 +- .../Adminhtml/Order/Invoice/Email.php | 2 +- .../Adminhtml/Order/Invoice/Grid.php | 2 +- .../Adminhtml/Order/Invoice/Index.php | 2 +- .../Adminhtml/Order/Invoice/NewAction.php | 2 +- .../Adminhtml/Order/Invoice/Pdfinvoices.php | 2 +- .../Adminhtml/Order/Invoice/PrintAction.php | 2 +- .../Adminhtml/Order/Invoice/Save.php | 2 +- .../Adminhtml/Order/Invoice/Start.php | 2 +- .../Adminhtml/Order/Invoice/UpdateQty.php | 2 +- .../Adminhtml/Order/Invoice/View.php | 2 +- .../Invoice/{Void.php => VoidAction.php} | 4 +- .../Controller/Adminhtml/Order/Invoices.php | 2 +- .../Controller/Adminhtml/Order/MassCancel.php | 2 +- .../Controller/Adminhtml/Order/MassHold.php | 2 +- .../Controller/Adminhtml/Order/MassUnhold.php | 2 +- .../Order/PdfDocumentsMassAction.php | 2 +- .../Adminhtml/Order/Pdfcreditmemos.php | 2 +- .../Controller/Adminhtml/Order/Pdfdocs.php | 2 +- .../Adminhtml/Order/Pdfinvoices.php | 2 +- .../Adminhtml/Order/Pdfshipments.php | 2 +- .../Adminhtml/Order/ReviewPayment.php | 2 +- .../Controller/Adminhtml/Order/Shipments.php | 2 +- .../Controller/Adminhtml/Order/Status.php | 2 +- .../Adminhtml/Order/Status/Assign.php | 2 +- .../Adminhtml/Order/Status/AssignPost.php | 2 +- .../Adminhtml/Order/Status/Edit.php | 2 +- .../Adminhtml/Order/Status/Index.php | 2 +- .../Adminhtml/Order/Status/NewAction.php | 2 +- .../Adminhtml/Order/Status/Save.php | 2 +- .../Adminhtml/Order/Status/Unassign.php | 2 +- .../Adminhtml/Order/Transactions.php | 2 +- .../Controller/Adminhtml/Order/Unhold.php | 2 +- .../Sales/Controller/Adminhtml/Order/View.php | 2 +- .../Adminhtml/Order/View/Giftmessage.php | 2 +- .../Adminhtml/Order/View/Giftmessage/Save.php | 2 +- .../Adminhtml/Order/VoidPayment.php | 2 +- .../Shipment/AbstractShipment/Index.php | 2 +- .../AbstractShipment/Pdfshipments.php | 2 +- .../Shipment/AbstractShipment/PrintAction.php | 2 +- .../Shipment/AbstractShipment/View.php | 2 +- .../Controller/Adminhtml/Shipment/Index.php | 2 +- .../Adminhtml/Shipment/Pdfshipments.php | 2 +- .../Adminhtml/Shipment/PrintAction.php | 2 +- .../Controller/Adminhtml/Shipment/View.php | 2 +- .../Controller/Adminhtml/Transactions.php | 2 +- .../Adminhtml/Transactions/Fetch.php | 2 +- .../Adminhtml/Transactions/Grid.php | 2 +- .../Adminhtml/Transactions/Index.php | 2 +- .../Adminhtml/Transactions/View.php | 2 +- .../Download/DownloadCustomOption.php | 35 +- .../Sales/Controller/Guest/Creditmemo.php | 2 +- .../Magento/Sales/Controller/Guest/Form.php | 2 +- .../Sales/Controller/Guest/Invoice.php | 2 +- .../Sales/Controller/Guest/OrderLoader.php | 2 +- .../Guest/OrderViewAuthorization.php | 2 +- .../Sales/Controller/Guest/PrintAction.php | 2 +- .../Controller/Guest/PrintCreditmemo.php | 2 +- .../Sales/Controller/Guest/PrintInvoice.php | 2 +- .../Sales/Controller/Guest/PrintShipment.php | 2 +- .../Sales/Controller/Guest/Reorder.php | 2 +- .../Sales/Controller/Guest/Shipment.php | 2 +- .../Magento/Sales/Controller/Guest/View.php | 2 +- .../Sales/Controller/Order/Creditmemo.php | 2 +- .../Sales/Controller/Order/History.php | 2 +- .../Sales/Controller/Order/Invoice.php | 2 +- .../Order/Plugin/Authentication.php | 2 +- .../Sales/Controller/Order/PrintAction.php | 2 +- .../Controller/Order/PrintCreditmemo.php | 2 +- .../Sales/Controller/Order/PrintInvoice.php | 2 +- .../Sales/Controller/Order/PrintShipment.php | 2 +- .../Sales/Controller/Order/Reorder.php | 2 +- .../Sales/Controller/Order/Shipment.php | 2 +- .../Magento/Sales/Controller/Order/View.php | 2 +- .../Sales/Controller/OrderInterface.php | 2 +- .../Magento/Sales/Cron/CleanExpiredQuotes.php | 2 +- .../Magento/Sales/Cron/GridAsyncInsert.php | 2 +- app/code/Magento/Sales/Cron/SendEmails.php | 2 +- .../Sales/CustomerData/LastOrderedItems.php | 17 +- .../Exception/CouldNotInvoiceException.php | 2 +- .../Exception/CouldNotRefundException.php | 2 +- .../Sales/Exception/CouldNotShipException.php | 2 +- .../Exception/DocumentValidationException.php | 2 +- app/code/Magento/Sales/Helper/Admin.php | 2 +- app/code/Magento/Sales/Helper/Data.php | 2 +- app/code/Magento/Sales/Helper/Guest.php | 2 +- app/code/Magento/Sales/Helper/Reorder.php | 2 +- .../Magento/Sales/Model/AbstractModel.php | 2 +- .../Magento/Sales/Model/AbstractNotifier.php | 2 +- .../Magento/Sales/Model/AdminOrder/Create.php | 29 +- .../Sales/Model/AdminOrder/EmailSender.php | 2 +- .../AdminOrder/Product/Quote/Initializer.php | 2 +- app/code/Magento/Sales/Model/Config.php | 2 +- .../Config/Backend/Email/AsyncSending.php | 2 +- .../Config/Backend/Grid/AsyncIndexing.php | 2 +- .../Magento/Sales/Model/Config/Converter.php | 2 +- app/code/Magento/Sales/Model/Config/Data.php | 21 +- .../Magento/Sales/Model/Config/Ordered.php | 19 +- .../Magento/Sales/Model/Config/Reader.php | 2 +- .../Sales/Model/Config/SchemaLocator.php | 2 +- .../Model/Config/Source/Order/Status.php | 2 +- .../Config/Source/Order/Status/NewStatus.php | 2 +- .../Source/Order/Status/Newprocessing.php | 2 +- .../Config/Source/Order/Status/Processing.php | 2 +- .../Magento/Sales/Model/ConfigInterface.php | 2 +- .../Magento/Sales/Model/Convert/Order.php | 2 +- .../AggregateSalesReportBestsellersData.php | 2 +- .../AggregateSalesReportInvoicedData.php | 2 +- .../CronJob/AggregateSalesReportOrderData.php | 2 +- .../AggregateSalesReportRefundedData.php | 2 +- .../Model/CronJob/CleanExpiredOrders.php | 2 +- app/code/Magento/Sales/Model/Download.php | 2 +- .../Sales/Model/EmailSenderHandler.php | 2 +- .../Magento/Sales/Model/EntityInterface.php | 2 +- .../Magento/Sales/Model/EntityStorage.php | 2 +- .../Model/Grid/Child/CollectionUpdater.php | 2 +- .../Sales/Model/Grid/CollectionUpdater.php | 2 +- .../Magento/Sales/Model/GridAsyncInsert.php | 2 +- app/code/Magento/Sales/Model/Increment.php | 2 +- app/code/Magento/Sales/Model/InvoiceOrder.php | 55 +- app/code/Magento/Sales/Model/Order.php | 31 +- .../Magento/Sales/Model/Order/Address.php | 2 +- .../Sales/Model/Order/Address/Renderer.php | 3 +- .../Sales/Model/Order/Address/Validator.php | 73 +- .../Sales/Model/Order/AddressRepository.php | 2 +- .../Magento/Sales/Model/Order/Admin/Item.php | 2 +- app/code/Magento/Sales/Model/Order/Config.php | 22 +- .../Magento/Sales/Model/Order/Creditmemo.php | 2 +- .../Sales/Model/Order/Creditmemo/Comment.php | 2 +- .../Order/Creditmemo/Comment/Validator.php | 2 +- .../Order/Creditmemo/CommentCreation.php | 2 +- .../Sales/Model/Order/Creditmemo/Config.php | 2 +- .../Order/Creditmemo/CreationArguments.php | 2 +- .../Order/Creditmemo/CreditmemoValidator.php | 2 +- .../CreditmemoValidatorInterface.php | 5 +- .../Sales/Model/Order/Creditmemo/Item.php | 2 +- .../Validation/CreationQuantityValidator.php | 2 +- .../Model/Order/Creditmemo/ItemCreation.php | 2 +- .../Creditmemo/ItemCreationValidator.php | 2 +- .../ItemCreationValidatorInterface.php | 5 +- .../Sales/Model/Order/Creditmemo/Notifier.php | 2 +- .../Order/Creditmemo/NotifierInterface.php | 2 +- .../Order/Creditmemo/RefundOperation.php | 2 +- .../Order/Creditmemo/Sender/EmailSender.php | 2 +- .../Order/Creditmemo/SenderInterface.php | 2 +- .../Order/Creditmemo/Total/AbstractTotal.php | 2 +- .../Model/Order/Creditmemo/Total/Cost.php | 2 +- .../Model/Order/Creditmemo/Total/Discount.php | 2 +- .../Model/Order/Creditmemo/Total/Grand.php | 2 +- .../Model/Order/Creditmemo/Total/Shipping.php | 2 +- .../Model/Order/Creditmemo/Total/Subtotal.php | 2 +- .../Model/Order/Creditmemo/Total/Tax.php | 2 +- .../Validation/QuantityValidator.php | 2 +- .../Creditmemo/Validation/TotalsValidator.php | 2 +- .../Model/Order/CreditmemoDocumentFactory.php | 4 +- .../Sales/Model/Order/CreditmemoFactory.php | 54 +- .../Sales/Model/Order/CreditmemoNotifier.php | 2 +- .../Model/Order/CreditmemoRepository.php | 2 +- .../Sales/Model/Order/CustomerManagement.php | 2 +- .../Model/Order/Email/Container/Container.php | 2 +- .../Container/CreditmemoCommentIdentity.php | 2 +- .../Email/Container/CreditmemoIdentity.php | 2 +- .../Email/Container/IdentityInterface.php | 2 +- .../Container/InvoiceCommentIdentity.php | 2 +- .../Order/Email/Container/InvoiceIdentity.php | 2 +- .../Email/Container/OrderCommentIdentity.php | 2 +- .../Order/Email/Container/OrderIdentity.php | 2 +- .../Container/ShipmentCommentIdentity.php | 2 +- .../Email/Container/ShipmentIdentity.php | 2 +- .../Model/Order/Email/Container/Template.php | 2 +- .../Sales/Model/Order/Email/NotifySender.php | 2 +- .../Sales/Model/Order/Email/Sender.php | 2 +- .../Email/Sender/CreditmemoCommentSender.php | 2 +- .../Order/Email/Sender/CreditmemoSender.php | 2 +- .../Email/Sender/InvoiceCommentSender.php | 2 +- .../Order/Email/Sender/InvoiceSender.php | 2 +- .../Order/Email/Sender/OrderCommentSender.php | 2 +- .../Model/Order/Email/Sender/OrderSender.php | 2 +- .../Email/Sender/ShipmentCommentSender.php | 2 +- .../Order/Email/Sender/ShipmentSender.php | 2 +- .../Sales/Model/Order/Email/SenderBuilder.php | 2 +- .../Order/Grid/Massaction/ItemsUpdater.php | 2 +- .../Model/Order/Grid/Row/UrlGenerator.php | 2 +- .../Magento/Sales/Model/Order/Invoice.php | 2 +- .../Sales/Model/Order/Invoice/Comment.php | 2 +- .../Model/Order/Invoice/Comment/Validator.php | 2 +- .../Model/Order/Invoice/CommentCreation.php | 2 +- .../Sales/Model/Order/Invoice/Config.php | 2 +- .../Model/Order/Invoice/CreationArguments.php | 2 +- .../Order/Invoice/Grid/Row/UrlGenerator.php | 2 +- .../Model/Order/Invoice/InvoiceValidator.php | 2 +- .../Invoice/InvoiceValidatorInterface.php | 5 +- .../Sales/Model/Order/Invoice/Item.php | 2 +- .../Model/Order/Invoice/ItemCreation.php | 2 +- .../Sales/Model/Order/Invoice/Notifier.php | 2 +- .../Model/Order/Invoice/NotifierInterface.php | 2 +- .../Model/Order/Invoice/PayOperation.php | 2 +- .../Order/Invoice/Plugin/AddressUpdate.php | 2 +- .../Order/Invoice/Sender/EmailSender.php | 2 +- .../Model/Order/Invoice/SenderInterface.php | 2 +- .../Order/Invoice/Total/AbstractTotal.php | 2 +- .../Sales/Model/Order/Invoice/Total/Cost.php | 2 +- .../Model/Order/Invoice/Total/Discount.php | 2 +- .../Sales/Model/Order/Invoice/Total/Grand.php | 2 +- .../Model/Order/Invoice/Total/Shipping.php | 2 +- .../Model/Order/Invoice/Total/Subtotal.php | 2 +- .../Sales/Model/Order/Invoice/Total/Tax.php | 2 +- .../Order/Invoice/Validation/CanRefund.php | 2 +- .../Model/Order/InvoiceDocumentFactory.php | 2 +- .../Sales/Model/Order/InvoiceNotifier.php | 2 +- .../Model/Order/InvoiceNotifierInterface.php | 2 +- .../Model/Order/InvoiceQuantityValidator.php | 2 +- .../Sales/Model/Order/InvoiceRepository.php | 2 +- .../Model/Order/InvoiceStatisticInterface.php | 2 +- app/code/Magento/Sales/Model/Order/Item.php | 17 +- .../Sales/Model/Order/ItemRepository.php | 2 +- .../Order/OrderStateResolverInterface.php | 2 +- .../Sales/Model/Order/OrderValidator.php | 2 +- .../Model/Order/OrderValidatorInterface.php | 5 +- .../Magento/Sales/Model/Order/Payment.php | 92 +- .../Sales/Model/Order/Payment/Info.php | 2 +- .../Payment/Operations/AbstractOperation.php | 2 +- .../Payment/Operations/AuthorizeOperation.php | 2 +- .../Payment/Operations/CaptureOperation.php | 2 +- .../Payment/Operations/OrderOperation.php | 2 +- .../RegisterCaptureNotificationOperation.php | 2 +- .../Sales/Model/Order/Payment/Processor.php | 2 +- .../Sales/Model/Order/Payment/Repository.php | 2 +- .../Order/Payment/State/AuthorizeCommand.php | 2 +- .../Order/Payment/State/CaptureCommand.php | 2 +- .../Order/Payment/State/CommandInterface.php | 2 +- .../Order/Payment/State/OrderCommand.php | 2 +- .../RegisterCaptureNotificationCommand.php | 2 +- .../Sales/Model/Order/Payment/Transaction.php | 2 +- .../Order/Payment/Transaction/Builder.php | 2 +- .../Payment/Transaction/BuilderInterface.php | 2 +- .../Order/Payment/Transaction/Manager.php | 2 +- .../Payment/Transaction/ManagerInterface.php | 2 +- .../Order/Payment/Transaction/Repository.php | 2 +- .../Sales/Model/Order/PaymentAdapter.php | 2 +- .../Model/Order/PaymentAdapterInterface.php | 2 +- .../Sales/Model/Order/Pdf/AbstractPdf.php | 7 +- .../Magento/Sales/Model/Order/Pdf/Config.php | 2 +- .../Model/Order/Pdf/Config/Converter.php | 2 +- .../Sales/Model/Order/Pdf/Config/Reader.php | 2 +- .../Model/Order/Pdf/Config/SchemaLocator.php | 2 +- .../Sales/Model/Order/Pdf/Creditmemo.php | 2 +- .../Magento/Sales/Model/Order/Pdf/Invoice.php | 2 +- .../Model/Order/Pdf/Items/AbstractItems.php | 2 +- .../Items/Creditmemo/DefaultCreditmemo.php | 2 +- .../Pdf/Items/Invoice/DefaultInvoice.php | 2 +- .../Pdf/Items/Shipment/DefaultShipment.php | 2 +- .../Sales/Model/Order/Pdf/ItemsFactory.php | 2 +- .../Sales/Model/Order/Pdf/Shipment.php | 2 +- .../Model/Order/Pdf/Total/DefaultTotal.php | 2 +- .../Sales/Model/Order/Pdf/Total/Factory.php | 2 +- .../Magento/Sales/Model/Order/Shipment.php | 2 +- .../Sales/Model/Order/Shipment/Comment.php | 2 +- .../Order/Shipment/Comment/Validator.php | 2 +- .../Model/Order/Shipment/CommentCreation.php | 2 +- .../Order/Shipment/CreationArguments.php | 2 +- .../Sales/Model/Order/Shipment/Item.php | 2 +- .../Model/Order/Shipment/ItemCreation.php | 2 +- .../Sales/Model/Order/Shipment/Notifier.php | 2 +- .../Order/Shipment/NotifierInterface.php | 2 +- .../Model/Order/Shipment/OrderRegistrar.php | 2 +- .../Shipment/OrderRegistrarInterface.php | 2 +- .../Sales/Model/Order/Shipment/Package.php | 2 +- .../Model/Order/Shipment/PackageCreation.php | 2 +- .../Order/Shipment/Sender/EmailSender.php | 2 +- .../Model/Order/Shipment/SenderInterface.php | 2 +- .../Order/Shipment/ShipmentValidator.php | 2 +- .../Shipment/ShipmentValidatorInterface.php | 5 +- .../Sales/Model/Order/Shipment/Track.php | 2 +- .../Model/Order/Shipment/Track/Validator.php | 2 +- .../Model/Order/Shipment/TrackCreation.php | 2 +- .../Shipment/Validation/QuantityValidator.php | 2 +- .../Shipment/Validation/TrackValidator.php | 2 +- .../Model/Order/ShipmentDocumentFactory.php | 2 +- .../Sales/Model/Order/ShipmentFactory.php | 18 +- .../Sales/Model/Order/ShipmentRepository.php | 2 +- .../Magento/Sales/Model/Order/Shipping.php | 2 +- .../Sales/Model/Order/ShippingAssignment.php | 2 +- .../Model/Order/ShippingAssignmentBuilder.php | 2 +- .../Sales/Model/Order/ShippingBuilder.php | 2 +- .../Sales/Model/Order/ShippingTotal.php | 2 +- .../Sales/Model/Order/StateResolver.php | 2 +- app/code/Magento/Sales/Model/Order/Status.php | 2 +- .../Sales/Model/Order/Status/History.php | 2 +- .../Model/Order/Status/History/Validator.php | 2 +- app/code/Magento/Sales/Model/Order/Tax.php | 2 +- .../Magento/Sales/Model/Order/Tax/Item.php | 2 +- app/code/Magento/Sales/Model/Order/Total.php | 2 +- .../Sales/Model/Order/Total/AbstractTotal.php | 2 +- .../Sales/Model/Order/Total/Config/Base.php | 10 +- .../Sales/Model/Order/TotalFactory.php | 2 +- .../Model/Order/Validation/CanInvoice.php | 2 +- .../Model/Order/Validation/CanRefund.php | 2 +- .../Sales/Model/Order/Validation/CanShip.php | 2 +- .../Model/Order/Validation/InvoiceOrder.php | 79 + .../Validation/InvoiceOrderInterface.php | 42 + .../Model/Order/Validation/RefundInvoice.php | 123 + .../Validation/RefundInvoiceInterface.php | 43 + .../Model/Order/Validation/RefundOrder.php | 104 + .../Order/Validation/RefundOrderInterface.php | 38 + .../Model/Order/Validation/ShipOrder.php | 92 + .../Order/Validation/ShipOrderInterface.php | 41 + .../Magento/Sales/Model/OrderNotifier.php | 2 +- .../Magento/Sales/Model/OrderRepository.php | 14 +- .../Magento/Sales/Model/RefundInvoice.php | 102 +- app/code/Magento/Sales/Model/RefundOrder.php | 80 +- .../Model/ResourceModel/AbstractGrid.php | 2 +- .../Sales/Model/ResourceModel/Attribute.php | 2 +- .../Collection/AbstractCollection.php | 2 +- .../Model/ResourceModel/EntityAbstract.php | 2 +- .../Sales/Model/ResourceModel/Grid.php | 2 +- .../Model/ResourceModel/Grid/Collection.php | 2 +- .../Model/ResourceModel/GridInterface.php | 2 +- .../Sales/Model/ResourceModel/GridPool.php | 2 +- .../Sales/Model/ResourceModel/Helper.php | 2 +- .../Model/ResourceModel/HelperInterface.php | 2 +- .../Sales/Model/ResourceModel/Metadata.php | 2 +- .../Sales/Model/ResourceModel/Order.php | 2 +- .../Model/ResourceModel/Order/Address.php | 2 +- .../Order/Address/Collection.php | 2 +- .../Order/Attribute/Backend/Billing.php | 2 +- .../Order/Attribute/Backend/Child.php | 2 +- .../Order/Attribute/Backend/Shipping.php | 2 +- .../Model/ResourceModel/Order/Collection.php | 2 +- .../Order/Collection/AbstractCollection.php | 2 +- .../Order/Collection/Factory.php | 2 +- .../ResourceModel/Order/CollectionFactory.php | 4 +- .../Order/CollectionFactoryInterface.php | 2 +- .../Comment/Collection/AbstractCollection.php | 2 +- .../Model/ResourceModel/Order/Creditmemo.php | 2 +- .../Creditmemo/Attribute/Backend/Child.php | 2 +- .../Order/Creditmemo/Collection.php | 2 +- .../Order/Creditmemo/Comment.php | 2 +- .../Order/Creditmemo/Comment/Collection.php | 2 +- .../Order/Creditmemo/Grid/Collection.php | 2 +- .../Order/Creditmemo/Grid/StatusList.php | 2 +- .../ResourceModel/Order/Creditmemo/Item.php | 2 +- .../Order/Creditmemo/Item/Collection.php | 2 +- .../Creditmemo/Order/Grid/Collection.php | 2 +- .../Order/Creditmemo/Relation.php | 2 +- .../Order/Creditmemo/Relation/Refund.php | 2 +- .../Order/Customer/Collection.php | 2 +- .../ResourceModel/Order/Grid/Collection.php | 2 +- .../ResourceModel/Order/Handler/Address.php | 2 +- .../ResourceModel/Order/Handler/State.php | 2 +- .../Model/ResourceModel/Order/Invoice.php | 2 +- .../Order/Invoice/Attribute/Backend/Child.php | 2 +- .../Order/Invoice/Attribute/Backend/Item.php | 2 +- .../Order/Invoice/Attribute/Backend/Order.php | 2 +- .../Order/Invoice/Collection.php | 2 +- .../ResourceModel/Order/Invoice/Comment.php | 2 +- .../Order/Invoice/Comment/Collection.php | 2 +- .../Order/Invoice/Grid/Collection.php | 2 +- .../Order/Invoice/Grid/StatusList.php | 2 +- .../ResourceModel/Order/Invoice/Item.php | 2 +- .../Order/Invoice/Item/Collection.php | 2 +- .../Order/Invoice/Orders/Grid/Collection.php | 2 +- .../ResourceModel/Order/Invoice/Relation.php | 2 +- .../Sales/Model/ResourceModel/Order/Item.php | 2 +- .../ResourceModel/Order/Item/Collection.php | 2 +- .../Model/ResourceModel/Order/Payment.php | 2 +- .../Order/Payment/Collection.php | 2 +- .../Order/Payment/Transaction.php | 2 +- .../Order/Payment/Transaction/Collection.php | 2 +- .../Order/Plugin/Authorization.php | 2 +- .../Model/ResourceModel/Order/Relation.php | 2 +- .../ResourceModel/Order/Rss/OrderStatus.php | 2 +- .../Model/ResourceModel/Order/Shipment.php | 2 +- .../Shipment/Attribute/Backend/Child.php | 2 +- .../Order/Shipment/Collection.php | 2 +- .../ResourceModel/Order/Shipment/Comment.php | 2 +- .../Order/Shipment/Comment/Collection.php | 2 +- .../Order/Shipment/Grid/Collection.php | 2 +- .../ResourceModel/Order/Shipment/Item.php | 2 +- .../Order/Shipment/Item/Collection.php | 2 +- .../Order/Shipment/Order/Grid/Collection.php | 2 +- .../ResourceModel/Order/Shipment/Relation.php | 2 +- .../ResourceModel/Order/Shipment/Track.php | 2 +- .../Order/Shipment/Track/Collection.php | 2 +- .../Model/ResourceModel/Order/Status.php | 2 +- .../ResourceModel/Order/Status/Collection.php | 2 +- .../ResourceModel/Order/Status/History.php | 2 +- .../Order/Status/History/Collection.php | 2 +- .../Sales/Model/ResourceModel/Order/Tax.php | 2 +- .../ResourceModel/Order/Tax/Collection.php | 2 +- .../Model/ResourceModel/Order/Tax/Item.php | 2 +- .../Sales/Model/ResourceModel/Report.php | 2 +- .../ResourceModel/Report/AbstractReport.php | 2 +- .../ResourceModel/Report/Bestsellers.php | 2 +- .../Report/Bestsellers/Collection.php | 2 +- .../Report/Collection/AbstractCollection.php | 2 +- .../Model/ResourceModel/Report/Invoiced.php | 2 +- .../Report/Invoiced/Collection/Invoiced.php | 2 +- .../Report/Invoiced/Collection/Order.php | 2 +- .../Model/ResourceModel/Report/Order.php | 2 +- .../ResourceModel/Report/Order/Collection.php | 2 +- .../ResourceModel/Report/Order/Createdat.php | 2 +- .../ResourceModel/Report/Order/Updatedat.php | 2 +- .../Report/Order/Updatedat/Collection.php | 2 +- .../Model/ResourceModel/Report/Refunded.php | 2 +- .../Report/Refunded/Collection/Order.php | 2 +- .../Report/Refunded/Collection/Refunded.php | 2 +- .../Model/ResourceModel/Report/Shipping.php | 2 +- .../Report/Shipping/Collection/Order.php | 2 +- .../Report/Shipping/Collection/Shipment.php | 2 +- .../Model/ResourceModel/Sale/Collection.php | 2 +- .../Model/ResourceModel/Status/Collection.php | 2 +- .../Transaction/Grid/Collection.php | 2 +- .../Transaction/Grid/TypeList.php | 2 +- app/code/Magento/Sales/Model/Rss/NewOrder.php | 2 +- .../Magento/Sales/Model/Rss/OrderStatus.php | 2 +- .../Sales/Model/Service/CreditmemoService.php | 4 +- .../Sales/Model/Service/InvoiceService.php | 2 +- .../Sales/Model/Service/OrderService.php | 4 +- .../Sales/Model/Service/ShipmentService.php | 2 +- app/code/Magento/Sales/Model/ShipOrder.php | 54 +- .../CreditmemoCommentResourceInterface.php | 2 +- .../Spi/CreditmemoItemResourceInterface.php | 2 +- .../Model/Spi/CreditmemoResourceInterface.php | 2 +- .../Spi/InvoiceCommentResourceInterface.php | 2 +- .../Spi/InvoiceItemResourceInterface.php | 2 +- .../Model/Spi/InvoiceResourceInterface.php | 2 +- .../Spi/OrderAddressResourceInterface.php | 2 +- .../Model/Spi/OrderItemResourceInterface.php | 2 +- .../Spi/OrderPaymentResourceInterface.php | 2 +- .../Model/Spi/OrderResourceInterface.php | 2 +- .../OrderStatusHistoryResourceInterface.php | 2 +- .../Spi/ShipmentCommentResourceInterface.php | 2 +- .../Spi/ShipmentItemResourceInterface.php | 2 +- .../Model/Spi/ShipmentResourceInterface.php | 2 +- .../Spi/ShipmentTrackResourceInterface.php | 2 +- .../Spi/TransactionResourceInterface.php | 2 +- .../Sales/Model/Status/ListFactory.php | 2 +- .../Magento/Sales/Model/Status/ListStatus.php | 2 +- app/code/Magento/Sales/Model/Validator.php | 23 +- .../Sales/Model/ValidatorInterface.php | 2 +- .../Magento/Sales/Model/ValidatorResult.php | 41 + .../Sales/Model/ValidatorResultInterface.php | 29 + .../Sales/Model/ValidatorResultMerger.php | 47 + .../Observer/Backend/CatalogPriceRule.php | 2 +- .../CatalogProductSaveAfterObserver.php | 2 +- .../Backend/SubtractQtyFromQuotesObserver.php | 2 +- .../AddVatRequestParamsOrderComment.php | 2 +- .../Frontend/RestoreCustomerGroupId.php | 2 +- .../Observer/GridAsyncInsertObserver.php | 2 +- .../Observer/GridProcessAddressChange.php | 2 +- .../Sales/Observer/GridSyncInsertObserver.php | 2 +- .../Sales/Observer/GridSyncRemoveObserver.php | 2 +- .../Sales/Observer/Virtual/SendEmails.php | 2 +- .../Setup/ConvertSerializedDataToJson.php | 113 + app/code/Magento/Sales/Setup/InstallData.php | 2 +- .../Magento/Sales/Setup/InstallSchema.php | 4 +- app/code/Magento/Sales/Setup/SalesSetup.php | 51 +- .../Sales/Setup/SerializedDataConverter.php | 45 + app/code/Magento/Sales/Setup/UpgradeData.php | 163 +- .../Magento/Sales/Setup/UpgradeSchema.php | 28 +- .../Adminhtml/Items/AbstractItemsTest.php | 2 +- .../Block/Adminhtml/Items/AbstractTest.php | 2 +- .../Items/Column/DefaultColumnTest.php | 2 +- .../Adminhtml/Order/Comments/ViewTest.php | 2 +- .../Order/Create/AbstractCreateTest.php | 2 +- .../Adminhtml/Order/Create/CustomerTest.php | 2 +- .../Adminhtml/Order/Create/Items/GridTest.php | 2 +- .../Create/Search/Grid/Renderer/QtyTest.php | 2 +- .../Create/Sidebar/AbstractSidebarTest.php | 2 +- .../Adminhtml/Order/Create/TotalsTest.php | 2 +- .../Order/Creditmemo/Create/ItemsTest.php | 2 +- .../Adminhtml/Order/Invoice/ViewTest.php | 2 +- .../Order/Status/Assign/FormTest.php | 2 +- .../Block/Adminhtml/Order/Totals/TaxTest.php | 2 +- .../Adminhtml/Order/View/GiftmessageTest.php | 2 +- .../Adminhtml/Order/View/HistoryTest.php | 2 +- .../Block/Adminhtml/Order/View/InfoTest.php | 2 +- .../Adminhtml/Order/View/Tab/HistoryTest.php | 63 +- .../Order/View/Tab/Stub/OnlineMethod.php | 2 +- .../Order/View/Tab/TransactionsTest.php | 2 +- .../Adminhtml/Rss/Order/Grid/LinkTest.php | 2 +- .../Sales/Test/Unit/Block/Guest/LinkTest.php | 2 +- .../Test/Unit/Block/Items/AbstractTest.php | 2 +- .../Unit/Block/Order/Create/TotalsTest.php | 2 +- .../Order/Email/Items/DefaultItemsTest.php | 2 +- .../Email/Items/Order/DefaultOrderTest.php | 2 +- .../Test/Unit/Block/Order/HistoryTest.php | 2 +- .../Unit/Block/Order/Info/Buttons/RssTest.php | 2 +- .../Item/Renderer/DefaultRendererTest.php | 2 +- .../Unit/Block/Order/PrintShipmentTest.php | 95 + .../Test/Unit/Block/Order/RecentTest.php | 2 +- .../Test/Unit/Block/Reorder/SidebarTest.php | 2 +- .../Block/Status/Grid/Column/StateTest.php | 91 + .../AbstractCreditmemo/EmailTest.php | 2 +- .../Invoice/AbstractInvoice/EmailTest.php | 2 +- .../Controller/Adminhtml/Order/CancelTest.php | 2 +- .../Order/Create/ProcessDataTest.php | 2 +- .../Order/Creditmemo/AddCommentTest.php | 2 +- .../Adminhtml/Order/Creditmemo/CancelTest.php | 2 +- .../Order/Creditmemo/NewActionTest.php | 2 +- .../Order/Creditmemo/PrintActionTest.php | 2 +- .../Adminhtml/Order/Creditmemo/SaveTest.php | 2 +- .../Order/Creditmemo/UpdateQtyTest.php | 2 +- .../Adminhtml/Order/Creditmemo/ViewTest.php | 2 +- .../{VoidTest.php => VoidActionTest.php} | 8 +- .../Adminhtml/Order/CreditmemoLoaderTest.php | 2 +- .../Controller/Adminhtml/Order/EmailTest.php | 2 +- .../Controller/Adminhtml/Order/HoldTest.php | 2 +- .../Order/Invoice/AddCommentTest.php | 2 +- .../Adminhtml/Order/Invoice/CancelTest.php | 2 +- .../Adminhtml/Order/Invoice/CaptureTest.php | 2 +- .../Adminhtml/Order/Invoice/NewActionTest.php | 2 +- .../Order/Invoice/PrintActionTest.php | 2 +- .../Adminhtml/Order/Invoice/SaveTest.php | 2 +- .../Adminhtml/Order/Invoice/UpdateQtyTest.php | 2 +- .../Adminhtml/Order/Invoice/ViewTest.php | 2 +- .../{VoidTest.php => VoidActionTest.php} | 8 +- .../Adminhtml/Order/MassCancelTest.php | 2 +- .../Adminhtml/Order/MassHoldTest.php | 2 +- .../Adminhtml/Order/MassUnholdTest.php | 2 +- .../Adminhtml/Order/ReviewPaymentTest.php | 2 +- .../Controller/Adminhtml/Order/UnholdTest.php | 2 +- .../Controller/Adminhtml/Order/ViewTest.php | 2 +- .../Adminhtml/PdfDocumentsMassActionTest.php | 2 +- .../Download/DownloadCustomOptionTest.php | 18 +- .../Test/Unit/Controller/Guest/ViewTest.php | 2 +- .../Test/Unit/Cron/CleanExpiredQuotesTest.php | 2 +- .../CustomerData/LastOrderedItemsTest.php | 163 + .../Sales/Test/Unit/Helper/AdminTest.php | 2 +- .../Sales/Test/Unit/Helper/DataTest.php | 2 +- .../Sales/Test/Unit/Helper/GuestTest.php | 2 +- .../Sales/Test/Unit/Helper/ReorderTest.php | 2 +- .../Test/Unit/Model/AbstractModelTest.php | 2 +- .../Test/Unit/Model/AdminOrder/CreateTest.php | 2 +- .../Unit/Model/AdminOrder/EmailSenderTest.php | 2 +- .../Product/Quote/InitializerTest.php | 2 +- .../Config/Backend/Email/AsyncSendingTest.php | 2 +- .../Config/Backend/Grid/AsyncIndexingTest.php | 2 +- .../Test/Unit/Model/Config/ConverterTest.php | 2 +- .../Sales/Test/Unit/Model/Config/DataTest.php | 35 +- .../Test/Unit/Model/Config/ReaderTest.php | 2 +- .../Unit/Model/Config/SchemaLocatorTest.php | 2 +- .../Model/Config/Source/Order/StatusTest.php | 2 +- .../Sales/Test/Unit/Model/Config/XsdTest.php | 2 +- .../Config/_files/core_totals_config.php | 2 +- .../Config/_files/custom_totals_config.php | 2 +- .../Model/Config/_files/sales_invalid.xml | 2 +- .../_files/sales_invalid_duplicates.xml | 2 +- .../Config/_files/sales_invalid_root_node.xml | 2 +- .../Config/_files/sales_invalid_scope.xml | 2 +- .../sales_invalid_without_attributes.xml | 2 +- .../Unit/Model/Config/_files/sales_valid.xml | 2 +- .../Sales/Test/Unit/Model/ConfigTest.php | 2 +- ...ggregateSalesReportBestsellersDataTest.php | 2 +- .../AggregateSalesReportInvoicedDataTest.php | 2 +- .../AggregateSalesReportOrderDataTest.php | 2 +- .../AggregateSalesReportRefundedDataTest.php | 2 +- .../Model/CronJob/CleanExpiredOrdersTest.php | 2 +- .../Sales/Test/Unit/Model/DownloadTest.php | 2 +- .../Unit/Model/EmailSenderHandlerTest.php | 2 +- .../Grid/Child/CollectionUpdaterTest.php | 2 +- .../Unit/Model/Grid/CollectionUpdaterTest.php | 2 +- .../Test/Unit/Model/GridAsyncInsertTest.php | 2 +- .../Sales/Test/Unit/Model/IncrementTest.php | 2 +- .../Test/Unit/Model/InvoiceOrderTest.php | 135 +- .../Test/Unit/Model/InvoiceRepositoryTest.php | 2 +- .../Unit/Model/Order/Address/RendererTest.php | 141 + .../Model/Order/Address/ValidatorTest.php | 17 +- .../Model/Order/AddressRepositoryTest.php | 2 +- .../Test/Unit/Model/Order/AddressTest.php | 2 +- .../Test/Unit/Model/Order/Admin/ItemTest.php | 2 +- .../Test/Unit/Model/Order/ConfigTest.php | 145 +- .../Creditmemo/Comment/ValidatorTest.php | 2 +- .../CreateQuantityValidatorTest.php | 2 +- .../Unit/Model/Order/Creditmemo/ItemTest.php | 2 +- .../Order/Creditmemo/RefundOperationTest.php | 2 +- .../Creditmemo/Sender/EmailSenderTest.php | 2 +- .../Model/Order/Creditmemo/Total/CostTest.php | 2 +- .../Order/Creditmemo/Total/DiscountTest.php | 2 +- .../Order/Creditmemo/Total/ShippingTest.php | 2 +- .../Order/Creditmemo/Total/SubtotalTest.php | 2 +- .../Model/Order/Creditmemo/Total/TaxTest.php | 2 +- .../Validation/QuantityValidatorTest.php | 2 +- .../Order/CreditmemoDocumentFactoryTest.php | 2 +- .../Model/Order/CreditmemoNotifierTest.php | 2 +- .../Model/Order/CreditmemoRepositoryTest.php | 2 +- .../Test/Unit/Model/Order/CreditmemoTest.php | 2 +- .../Model/Order/CustomerManagementTest.php | 2 +- .../CreditmemoCommentIdentityTest.php | 2 +- .../Container/CreditmemoIdentityTest.php | 2 +- .../Container/InvoiceCommentIdentityTest.php | 2 +- .../Email/Container/InvoiceIdentityTest.php | 2 +- .../Container/OrderCommentIdentityTest.php | 2 +- .../Email/Container/OrderIdentityTest.php | 2 +- .../Container/ShipmentCommentIdentityTest.php | 2 +- .../Email/Container/ShipmentIdentityTest.php | 2 +- .../Order/Email/Container/TemplateTest.php | 2 +- .../Order/Email/Sender/AbstractSenderTest.php | 2 +- .../Sender/CreditmemoCommentSenderTest.php | 2 +- .../Email/Sender/CreditmemoSenderTest.php | 2 +- .../Email/Sender/InvoiceCommentSenderTest.php | 2 +- .../Order/Email/Sender/InvoiceSenderTest.php | 2 +- .../Email/Sender/OrderCommentSenderTest.php | 2 +- .../Order/Email/Sender/OrderSenderTest.php | 2 +- .../Sender/ShipmentCommentSenderTest.php | 2 +- .../Order/Email/Sender/ShipmentSenderTest.php | 2 +- .../Model/Order/Email/SenderBuilderTest.php | 2 +- .../Email/Stub/TransportInterfaceMock.php | 2 +- .../Grid/Massaction/ItemsUpdaterTest.php | 2 +- .../Model/Order/Grid/Row/UrlGeneratorTest.php | 2 +- .../Order/Invoice/Comment/ValidatorTest.php | 2 +- .../Invoice/Grid/Row/UrlGeneratorTest.php | 2 +- .../Unit/Model/Order/Invoice/ItemTest.php | 2 +- .../Model/Order/Invoice/PayOperationTest.php | 2 +- .../Invoice/Plugin/AddressUpdateTest.php | 2 +- .../Order/Invoice/Sender/EmailSenderTest.php | 2 +- .../Order/Invoice/Total/ShippingTest.php | 2 +- .../Model/Order/Invoice/Total/TaxTest.php | 2 +- .../Invoice/Validation/CanRefundTest.php | 2 +- .../Order/InvoiceDocumentFactoryTest.php | 2 +- .../Unit/Model/Order/InvoiceNotifierTest.php | 2 +- .../Order/InvoiceQuantityValidatorTest.php | 2 +- .../Test/Unit/Model/Order/InvoiceTest.php | 2 +- .../Unit/Model/Order/ItemRepositoryTest.php | 2 +- .../Sales/Test/Unit/Model/Order/ItemTest.php | 62 +- .../Unit/Model/Order/Payment/InfoTest.php | 2 +- .../Operations/CaptureOperationTest.php | 2 +- .../Model/Order/Payment/RepositoryTest.php | 2 +- .../Payment/State/AuthorizeCommandTest.php | 2 +- .../Payment/State/CaptureCommandTest.php | 2 +- .../Order/Payment/Transaction/BuilderTest.php | 2 +- .../Order/Payment/Transaction/ManagerTest.php | 2 +- .../Payment/Transaction/RepositoryTest.php | 2 +- .../Model/Order/Payment/TransactionTest.php | 2 +- .../Unit/Model/Order/PaymentAdapterTest.php | 2 +- .../Test/Unit/Model/Order/PaymentTest.php | 32 +- .../Unit/Model/Order/Pdf/AbstractTest.php | 2 +- .../Model/Order/Pdf/Config/ConverterTest.php | 2 +- .../Model/Order/Pdf/Config/ReaderTest.php | 2 +- .../Order/Pdf/Config/SchemaLocatorTest.php | 2 +- .../Unit/Model/Order/Pdf/Config/XsdTest.php | 2 +- .../Order/Pdf/Config/_files/pdf_merged.php | 2 +- .../Order/Pdf/Config/_files/pdf_merged.xml | 2 +- .../Model/Order/Pdf/Config/_files/pdf_one.xml | 2 +- .../Model/Order/Pdf/Config/_files/pdf_two.xml | 2 +- .../Test/Unit/Model/Order/Pdf/ConfigTest.php | 2 +- .../Test/Unit/Model/Order/Pdf/InvoiceTest.php | 2 +- .../Model/Order/Pdf/Total/FactoryTest.php | 2 +- .../Order/Shipment/Comment/ValidatorTest.php | 2 +- .../Order/Shipment/OrderRegistrarTest.php | 2 +- .../Order/Shipment/Sender/EmailSenderTest.php | 2 +- .../Order/Shipment/Track/ValidatorTest.php | 2 +- .../Unit/Model/Order/Shipment/TrackTest.php | 2 +- .../Validation/QuantityValidatorTest.php | 2 +- .../Validation/TrackValidatorTest.php | 2 +- .../Order/ShipmentDocumentFactoryTest.php | 2 +- .../Unit/Model/Order/ShipmentFactoryTest.php | 2 +- .../Model/Order/ShipmentRepositoryTest.php | 2 +- .../Test/Unit/Model/Order/ShipmentTest.php | 2 +- .../Unit/Model/Order/StateResolverTest.php | 2 +- .../Order/Status/History/ValidatorTest.php | 2 +- .../Unit/Model/Order/Status/HistoryTest.php | 2 +- .../Test/Unit/Model/Order/StatusTest.php | 2 +- .../Model/Order/Total/Config/BaseTest.php | 34 +- .../Model/Order/Validation/CanInvoiceTest.php | 2 +- .../Model/Order/Validation/CanRefundTest.php | 2 +- .../Model/Order/Validation/CanShipTest.php | 2 +- .../Test/Unit/Model/OrderNotifierTest.php | 2 +- .../Test/Unit/Model/OrderRepositoryTest.php | 38 +- .../Sales/Test/Unit/Model/OrderTest.php | 126 +- .../Test/Unit/Model/RefundInvoiceTest.php | 172 +- .../Sales/Test/Unit/Model/RefundOrderTest.php | 134 +- .../Model/ResourceModel/AttributeTest.php | 2 +- .../Unit/Model/ResourceModel/GridPoolTest.php | 2 +- .../Unit/Model/ResourceModel/HelperTest.php | 2 +- .../Model/ResourceModel/Order/AddressTest.php | 2 +- .../Order/Creditmemo/CommentTest.php | 2 +- .../Order/Creditmemo/Relation/RefundTest.php | 2 +- .../Order/Creditmemo/RelationTest.php | 2 +- .../Order/Handler/AddressTest.php | 2 +- .../ResourceModel/Order/Handler/StateTest.php | 2 +- .../Order/Invoice/CommentTest.php | 2 +- .../Order/Invoice/RelationTest.php | 2 +- .../Order/Plugin/AuthorizationTest.php | 2 +- .../ResourceModel/Order/RelationTest.php | 2 +- .../Order/Shipment/CommentTest.php | 2 +- .../Order/Shipment/RelationTest.php | 2 +- .../Order/Shipment/TrackTest.php | 2 +- .../Order/Status/History/CollectionTest.php | 2 +- .../Order/Status/HistoryTest.php | 2 +- .../Model/ResourceModel/Order/StatusTest.php | 2 +- .../ResourceModel/Order/Tax/ItemTest.php | 2 +- .../Unit/Model/ResourceModel/OrderTest.php | 2 +- .../Test/Unit/Model/Rss/NewOrderTest.php | 2 +- .../Test/Unit/Model/Rss/OrderStatusTest.php | 2 +- .../Model/Service/CreditmemoServiceTest.php | 2 +- .../Unit/Model/Service/InvoiceServiceTest.php | 2 +- .../Unit/Model/Service/OrderServiceTest.php | 2 +- .../Model/Service/ShipmentServiceTest.php | 2 +- .../Sales/Test/Unit/Model/ShipOrderTest.php | 185 +- .../Test/Unit/Model/Status/ListStatusTest.php | 2 +- .../Observer/Backend/CatalogPriceRuleTest.php | 2 +- .../CatalogProductSaveAfterObserverTest.php | 2 +- .../SubtractQtyFromQuotesObserverTest.php | 2 +- .../AddVatRequestParamsOrderCommentTest.php | 2 +- .../Frontend/RestoreCustomerGroupIdTest.php | 2 +- .../Observer/GridProcessAddressChangeTest.php | 2 +- .../Observer/GridSyncInsertObserverTest.php | 2 +- .../Observer/GridSyncRemoveObserverTest.php | 2 +- .../Setup/SerializedDataConverterTest.php | 124 + .../Component/DataProvider/DocumentTest.php | 2 +- .../Component/Listing/Column/AddressTest.php | 2 +- .../Listing/Column/CustomerGroupTest.php | 2 +- .../Listing/Column/PaymentMethodTest.php | 2 +- .../Ui/Component/Listing/Column/PriceTest.php | 2 +- .../Listing/Column/PurchasedPriceTest.php | 2 +- .../Listing/Column/Status/OptionsTest.php | 2 +- .../Component/Listing/Column/StatusTest.php | 2 +- .../Listing/Column/ViewActionTest.php | 2 +- .../Sales/Ui/Component/Control/PdfAction.php | 2 +- .../Ui/Component/DataProvider/Document.php | 2 +- .../Ui/Component/Listing/Column/Address.php | 2 +- .../Listing/Column/Creditmemo/State.php | 2 +- .../Column/Creditmemo/State/Options.php | 2 +- .../Listing/Column/CustomerGroup.php | 2 +- .../Listing/Column/Invoice/State.php | 2 +- .../Listing/Column/Invoice/State/Options.php | 2 +- .../Listing/Column/PaymentMethod.php | 2 +- .../Ui/Component/Listing/Column/Price.php | 2 +- .../Listing/Column/PurchasedPrice.php | 2 +- .../Ui/Component/Listing/Column/Status.php | 2 +- .../Listing/Column/Status/Options.php | 2 +- .../Component/Listing/Column/ViewAction.php | 2 +- app/code/Magento/Sales/composer.json | 2 +- app/code/Magento/Sales/etc/acl.xml | 2 +- app/code/Magento/Sales/etc/adminhtml/di.xml | 2 +- .../Magento/Sales/etc/adminhtml/events.xml | 2 +- app/code/Magento/Sales/etc/adminhtml/menu.xml | 2 +- .../Magento/Sales/etc/adminhtml/routes.xml | 2 +- .../Magento/Sales/etc/adminhtml/system.xml | 2 +- .../Magento/Sales/etc/catalog_attributes.xml | 2 +- app/code/Magento/Sales/etc/config.xml | 3 +- app/code/Magento/Sales/etc/crontab.xml | 2 +- app/code/Magento/Sales/etc/di.xml | 21 +- .../Magento/Sales/etc/email_templates.xml | 2 +- app/code/Magento/Sales/etc/events.xml | 2 +- .../Sales/etc/extension_attributes.xml | 2 +- app/code/Magento/Sales/etc/fieldset.xml | 2 +- app/code/Magento/Sales/etc/frontend/di.xml | 2 +- .../Magento/Sales/etc/frontend/events.xml | 2 +- .../Magento/Sales/etc/frontend/page_types.xml | 2 +- .../Magento/Sales/etc/frontend/routes.xml | 2 +- .../Magento/Sales/etc/frontend/sections.xml | 2 +- app/code/Magento/Sales/etc/module.xml | 4 +- app/code/Magento/Sales/etc/pdf.xml | 2 +- app/code/Magento/Sales/etc/pdf.xsd | 2 +- app/code/Magento/Sales/etc/pdf_file.xsd | 2 +- app/code/Magento/Sales/etc/resources.xml | 2 +- app/code/Magento/Sales/etc/sales.xml | 2 +- app/code/Magento/Sales/etc/sales.xsd | 2 +- app/code/Magento/Sales/etc/webapi.xml | 2 +- app/code/Magento/Sales/etc/webapi_rest/di.xml | 2 +- .../Magento/Sales/etc/webapi_rest/events.xml | 2 +- app/code/Magento/Sales/etc/webapi_soap/di.xml | 2 +- .../Magento/Sales/etc/webapi_soap/events.xml | 2 +- app/code/Magento/Sales/etc/widget.xml | 2 +- app/code/Magento/Sales/registration.php | 2 +- .../adminhtml/layout/customer_index_edit.xml | 2 +- .../layout/sales_creditmemo_exportcsv.xml | 2 +- .../layout/sales_creditmemo_exportexcel.xml | 2 +- .../layout/sales_creditmemo_grid.xml | 2 +- .../layout/sales_creditmemo_index.xml | 2 +- .../layout/sales_creditmemo_item_price.xml | 2 +- .../layout/sales_invoice_exportcsv.xml | 2 +- .../layout/sales_invoice_exportexcel.xml | 2 +- .../adminhtml/layout/sales_invoice_grid.xml | 2 +- .../adminhtml/layout/sales_invoice_index.xml | 2 +- .../layout/sales_invoice_item_price.xml | 2 +- .../layout/sales_order_addcomment.xml | 2 +- .../adminhtml/layout/sales_order_address.xml | 2 +- .../sales_order_create_customer_block.xml | 2 +- .../layout/sales_order_create_index.xml | 4 +- .../layout/sales_order_create_item_price.xml | 2 +- ...rder_create_load_block_billing_address.xml | 2 +- ...order_create_load_block_billing_method.xml | 2 +- .../sales_order_create_load_block_comment.xml | 2 +- ..._order_create_load_block_customer_grid.xml | 2 +- .../sales_order_create_load_block_data.xml | 2 +- ...s_order_create_load_block_form_account.xml | 2 +- ...es_order_create_load_block_giftmessage.xml | 2 +- .../sales_order_create_load_block_header.xml | 2 +- .../sales_order_create_load_block_items.xml | 2 +- .../sales_order_create_load_block_json.xml | 2 +- .../sales_order_create_load_block_message.xml | 2 +- ...les_order_create_load_block_newsletter.xml | 2 +- .../sales_order_create_load_block_plain.xml | 2 +- .../sales_order_create_load_block_search.xml | 2 +- ...es_order_create_load_block_search_grid.xml | 2 +- ...der_create_load_block_shipping_address.xml | 2 +- ...rder_create_load_block_shipping_method.xml | 2 +- .../sales_order_create_load_block_sidebar.xml | 2 +- ...s_order_create_load_block_sidebar_cart.xml | 2 +- ...der_create_load_block_sidebar_compared.xml | 2 +- ...er_create_load_block_sidebar_pcompared.xml | 2 +- ...rder_create_load_block_sidebar_pviewed.xml | 2 +- ...rder_create_load_block_sidebar_reorder.xml | 2 +- ...order_create_load_block_sidebar_viewed.xml | 2 +- ...der_create_load_block_sidebar_wishlist.xml | 2 +- .../sales_order_create_load_block_totals.xml | 2 +- .../sales_order_creditmemo_addcomment.xml | 2 +- .../sales_order_creditmemo_grid_block.xml | 2 +- .../layout/sales_order_creditmemo_new.xml | 2 +- .../sales_order_creditmemo_updateqty.xml | 2 +- .../layout/sales_order_creditmemo_view.xml | 2 +- .../layout/sales_order_creditmemos.xml | 2 +- .../layout/sales_order_edit_index.xml | 2 +- .../layout/sales_order_exportcsv.xml | 2 +- .../layout/sales_order_exportexcel.xml | 2 +- .../adminhtml/layout/sales_order_grid.xml | 2 +- .../adminhtml/layout/sales_order_index.xml | 2 +- .../layout/sales_order_invoice_addcomment.xml | 2 +- .../layout/sales_order_invoice_grid_block.xml | 2 +- .../layout/sales_order_invoice_new.xml | 2 +- .../layout/sales_order_invoice_updateqty.xml | 2 +- .../layout/sales_order_invoice_view.xml | 2 +- .../adminhtml/layout/sales_order_invoices.xml | 2 +- .../layout/sales_order_item_price.xml | 2 +- .../sales_order_shipment_grid_block.xml | 2 +- .../layout/sales_order_shipments.xml | 2 +- .../layout/sales_order_status_assign.xml | 2 +- .../layout/sales_order_status_edit.xml | 2 +- .../layout/sales_order_status_index.xml | 2 +- .../layout/sales_order_status_new.xml | 2 +- .../layout/sales_order_transactions.xml | 2 +- .../sales_order_transactions_grid_block.xml | 2 +- .../adminhtml/layout/sales_order_view.xml | 7 +- .../layout/sales_shipment_exportcsv.xml | 2 +- .../layout/sales_shipment_exportexcel.xml | 2 +- .../adminhtml/layout/sales_shipment_index.xml | 4 +- .../layout/sales_transaction_child_block.xml | 2 +- .../layout/sales_transactions_grid.xml | 2 +- .../layout/sales_transactions_grid_block.xml | 2 +- .../layout/sales_transactions_index.xml | 2 +- .../layout/sales_transactions_view.xml | 2 +- .../Sales/view/adminhtml/requirejs-config.js | 4 +- .../templates/items/column/name.phtml | 2 +- .../templates/items/column/qty.phtml | 2 +- .../adminhtml/templates/items/price/row.phtml | 2 +- .../templates/items/price/total.phtml | 2 +- .../templates/items/price/unit.phtml | 2 +- .../templates/items/renderer/default.phtml | 2 +- .../templates/order/address/form.phtml | 2 +- .../templates/order/comments/view.phtml | 2 +- .../templates/order/create/abstract.phtml | 2 +- .../order/create/billing/method/form.phtml | 6 +- .../templates/order/create/comment.phtml | 2 +- .../templates/order/create/coupons/form.phtml | 2 +- .../templates/order/create/data.phtml | 2 +- .../templates/order/create/form.phtml | 4 +- .../templates/order/create/form/account.phtml | 2 +- .../templates/order/create/form/address.phtml | 2 +- .../templates/order/create/giftmessage.phtml | 2 +- .../templates/order/create/items.phtml | 2 +- .../templates/order/create/items/grid.phtml | 2 +- .../order/create/items/price/row.phtml | 2 +- .../order/create/items/price/total.phtml | 2 +- .../order/create/items/price/unit.phtml | 2 +- .../adminhtml/templates/order/create/js.phtml | 2 +- .../order/create/newsletter/form.phtml | 2 +- .../order/create/shipping/method/form.phtml | 2 +- .../templates/order/create/sidebar.phtml | 2 +- .../order/create/sidebar/items.phtml | 10 +- .../templates/order/create/store/select.phtml | 2 +- .../templates/order/create/totals.phtml | 2 +- .../order/create/totals/default.phtml | 2 +- .../order/create/totals/grandtotal.phtml | 2 +- .../order/create/totals/shipping.phtml | 2 +- .../order/create/totals/subtotal.phtml | 2 +- .../templates/order/create/totals/tax.phtml | 2 +- .../order/creditmemo/create/form.phtml | 2 +- .../order/creditmemo/create/items.phtml | 2 +- .../create/items/renderer/default.phtml | 2 +- .../create/totals/adjustments.phtml | 2 +- .../order/creditmemo/view/form.phtml | 2 +- .../order/creditmemo/view/items.phtml | 2 +- .../view/items/renderer/default.phtml | 2 +- .../adminhtml/templates/order/details.phtml | 2 +- .../templates/order/giftoptions.phtml | 2 +- .../templates/order/invoice/create/form.phtml | 2 +- .../order/invoice/create/items.phtml | 2 +- .../create/items/renderer/default.phtml | 2 +- .../templates/order/invoice/view/form.phtml | 2 +- .../templates/order/invoice/view/items.phtml | 2 +- .../invoice/view/items/renderer/default.phtml | 2 +- .../adminhtml/templates/order/totalbar.phtml | 2 +- .../adminhtml/templates/order/totals.phtml | 2 +- .../templates/order/totals/discount.phtml | 2 +- .../templates/order/totals/due.phtml | 2 +- .../templates/order/totals/footer.phtml | 2 +- .../templates/order/totals/grand.phtml | 2 +- .../templates/order/totals/item.phtml | 2 +- .../templates/order/totals/main.phtml | 2 +- .../templates/order/totals/paid.phtml | 2 +- .../templates/order/totals/refunded.phtml | 2 +- .../templates/order/totals/shipping.phtml | 2 +- .../templates/order/totals/tax.phtml | 2 +- .../adminhtml/templates/order/view/form.phtml | 2 +- .../templates/order/view/giftmessage.phtml | 2 +- .../templates/order/view/history.phtml | 2 +- .../adminhtml/templates/order/view/info.phtml | 19 +- .../templates/order/view/items.phtml | 2 +- .../order/view/items/renderer/default.phtml | 2 +- .../templates/order/view/tab/history.phtml | 2 +- .../templates/order/view/tab/info.phtml | 4 +- .../templates/page/js/components.phtml | 2 +- .../templates/rss/order/grid/link.phtml | 2 +- .../templates/transactions/detail.phtml | 2 +- .../sales_order_creditmemo_grid.xml | 2 +- .../ui_component/sales_order_grid.xml | 6 +- .../ui_component/sales_order_invoice_grid.xml | 2 +- .../sales_order_shipment_grid.xml | 2 +- .../sales_order_view_creditmemo_grid.xml | 2 +- .../sales_order_view_invoice_grid.xml | 2 +- .../sales_order_view_shipment_grid.xml | 2 +- .../web/js/bootstrap/order-create-index.js | 6 +- .../web/js/bootstrap/order-post-action.js | 2 +- .../view/adminhtml/web/order/create/form.js | 5 +- .../adminhtml/web/order/create/giftmessage.js | 5 +- .../adminhtml/web/order/create/scripts.js | 4 +- .../adminhtml/web/order/edit/address/form.js | 2 +- .../view/adminhtml/web/order/edit/message.js | 26 +- .../web/order/giftoptions_tooltip.js | 376 +- .../adminhtml/web/order/view/post-wrapper.js | 2 +- .../view/frontend/email/creditmemo_new.html | 2 +- .../frontend/email/creditmemo_new_guest.html | 4 +- .../frontend/email/creditmemo_update.html | 2 +- .../email/creditmemo_update_guest.html | 2 +- .../view/frontend/email/invoice_new.html | 2 +- .../frontend/email/invoice_new_guest.html | 2 +- .../view/frontend/email/invoice_update.html | 2 +- .../frontend/email/invoice_update_guest.html | 2 +- .../Sales/view/frontend/email/order_new.html | 2 +- .../view/frontend/email/order_new_guest.html | 2 +- .../view/frontend/email/order_update.html | 2 +- .../frontend/email/order_update_guest.html | 2 +- .../view/frontend/email/shipment_new.html | 2 +- .../frontend/email/shipment_new_guest.html | 2 +- .../view/frontend/email/shipment_update.html | 2 +- .../frontend/email/shipment_update_guest.html | 2 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../view/frontend/layout/customer_account.xml | 5 +- .../layout/customer_account_index.xml | 2 +- .../Sales/view/frontend/layout/default.xml | 2 +- .../layout/sales_email_item_price.xml | 2 +- .../sales_email_order_creditmemo_items.xml | 2 +- ...sales_email_order_creditmemo_renderers.xml | 2 +- .../sales_email_order_invoice_items.xml | 2 +- .../sales_email_order_invoice_renderers.xml | 2 +- .../layout/sales_email_order_items.xml | 2 +- .../layout/sales_email_order_renderers.xml | 2 +- .../sales_email_order_shipment_items.xml | 2 +- .../sales_email_order_shipment_renderers.xml | 2 +- .../layout/sales_guest_creditmemo.xml | 2 +- .../view/frontend/layout/sales_guest_form.xml | 2 +- .../frontend/layout/sales_guest_invoice.xml | 2 +- .../frontend/layout/sales_guest_print.xml | 2 +- .../layout/sales_guest_printcreditmemo.xml | 2 +- .../layout/sales_guest_printinvoice.xml | 2 +- .../layout/sales_guest_printshipment.xml | 2 +- .../frontend/layout/sales_guest_reorder.xml | 2 +- .../frontend/layout/sales_guest_shipment.xml | 2 +- .../view/frontend/layout/sales_guest_view.xml | 3 +- .../layout/sales_order_creditmemo.xml | 2 +- .../sales_order_creditmemo_renderers.xml | 2 +- .../layout/sales_order_guest_info_links.xml | 2 +- .../frontend/layout/sales_order_history.xml | 2 +- .../layout/sales_order_info_links.xml | 2 +- .../frontend/layout/sales_order_invoice.xml | 2 +- .../layout/sales_order_invoice_renderers.xml | 2 +- .../layout/sales_order_item_price.xml | 2 +- .../layout/sales_order_item_renderers.xml | 2 +- .../frontend/layout/sales_order_print.xml | 2 +- ...sales_order_print_creditmemo_renderers.xml | 2 +- .../sales_order_print_invoice_renderers.xml | 2 +- .../layout/sales_order_print_renderers.xml | 2 +- .../sales_order_print_shipment_renderers.xml | 2 +- .../layout/sales_order_printcreditmemo.xml | 2 +- .../layout/sales_order_printinvoice.xml | 2 +- .../layout/sales_order_printshipment.xml | 2 +- .../frontend/layout/sales_order_reorder.xml | 2 +- .../frontend/layout/sales_order_shipment.xml | 2 +- .../layout/sales_order_shipment_renderers.xml | 2 +- .../view/frontend/layout/sales_order_view.xml | 3 +- .../Sales/view/frontend/requirejs-config.js | 4 +- .../templates/email/creditmemo/items.phtml | 2 +- .../templates/email/invoice/items.phtml | 2 +- .../view/frontend/templates/email/items.phtml | 2 +- .../email/items/creditmemo/default.phtml | 2 +- .../email/items/invoice/default.phtml | 2 +- .../templates/email/items/order/default.phtml | 2 +- .../templates/email/items/price/row.phtml | 2 +- .../email/items/shipment/default.phtml | 2 +- .../templates/email/shipment/items.phtml | 2 +- .../templates/email/shipment/track.phtml | 2 +- .../view/frontend/templates/guest/form.phtml | 2 +- .../frontend/templates/items/price/row.phtml | 2 +- .../items/price/total_after_discount.phtml | 2 +- .../frontend/templates/items/price/unit.phtml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../frontend/templates/order/comments.phtml | 2 +- .../frontend/templates/order/creditmemo.phtml | 2 +- .../templates/order/creditmemo/items.phtml | 2 +- .../creditmemo/items/renderer/default.phtml | 2 +- .../frontend/templates/order/history.phtml | 2 +- .../view/frontend/templates/order/info.phtml | 2 +- .../templates/order/info/buttons.phtml | 2 +- .../templates/order/info/buttons/rss.phtml | 2 +- .../frontend/templates/order/invoice.phtml | 2 +- .../templates/order/invoice/items.phtml | 2 +- .../invoice/items/renderer/default.phtml | 2 +- .../view/frontend/templates/order/items.phtml | 59 +- .../order/items/renderer/default.phtml | 2 +- .../templates/order/order_comments.phtml | 2 +- .../frontend/templates/order/order_date.phtml | 2 +- .../templates/order/order_status.phtml | 2 +- .../templates/order/print/creditmemo.phtml | 2 +- .../templates/order/print/invoice.phtml | 2 +- .../templates/order/print/shipment.phtml | 2 +- .../frontend/templates/order/recent.phtml | 2 +- .../shipment/items/renderer/default.phtml | 2 +- .../frontend/templates/order/totals.phtml | 2 +- .../view/frontend/templates/order/view.phtml | 2 +- .../frontend/templates/reorder/sidebar.phtml | 2 +- .../templates/widget/guest/form.phtml | 2 +- .../Sales/view/frontend/web/gift-message.js | 33 +- .../web/js/view/last-ordered-items.js | 9 +- .../Sales/view/frontend/web/orders-returns.js | 22 +- app/code/Magento/SalesAnalytics/LICENSE.txt | 48 + .../Magento/SalesAnalytics/LICENSE_AFL.txt | 48 + app/code/Magento/SalesAnalytics/README.md | 4 + app/code/Magento/SalesAnalytics/composer.json | 26 + .../Magento/SalesAnalytics/etc/analytics.xml | 18 + .../Magento/SalesAnalytics/etc/module.xml | 15 + .../Magento/SalesAnalytics/etc/reports.xml | 28 + .../Magento/SalesAnalytics/registration.php | 11 + app/code/Magento/SalesInventory/LICENSE.txt | 48 + .../Magento/SalesInventory/LICENSE_AFL.txt | 48 + .../Model/Order/ReturnProcessor.php | 123 + .../Model/Order/ReturnValidator.php | 70 + .../Plugin/Order/ReturnToStockInvoice.php | 104 + .../Model/Plugin/Order/ReturnToStockOrder.php | 100 + .../InvoiceRefundCreationArguments.php | 89 + .../OrderRefundCreationArguments.php | 83 + .../Observer/RefundOrderInventoryObserver.php | 69 +- app/code/Magento/SalesInventory/README.md | 1 + .../Unit/Model/Order/ReturnProcessorTest.php | 183 + .../Unit/Model/Order/ReturnValidatorTest.php | 134 + .../Plugin/Order/ReturnToStockInvoiceTest.php | 179 + .../Plugin/Order/ReturnToStockOrderTest.php | 158 + .../InvoiceRefundCreationArgumentsTest.php | 150 + .../OrderRefundCreationArgumentsTest.php | 141 + .../RefundOrderInventoryObserverTest.php | 111 +- app/code/Magento/SalesInventory/composer.json | 26 + app/code/Magento/SalesInventory/etc/di.xml | 21 + .../Magento/SalesInventory/etc/events.xml | 12 + .../etc/extension_attributes.xml | 12 + .../Magento/SalesInventory/etc/module.xml | 17 + .../Magento/SalesInventory/registration.php | 11 + .../Api/CouponManagementInterface.php | 2 +- .../Api/CouponRepositoryInterface.php | 2 +- .../SalesRule/Api/Data/ConditionInterface.php | 2 +- .../Data/CouponGenerationSpecInterface.php | 2 +- .../SalesRule/Api/Data/CouponInterface.php | 2 +- .../Data/CouponMassDeleteResultInterface.php | 2 +- .../Api/Data/CouponSearchResultInterface.php | 2 +- .../SalesRule/Api/Data/RuleInterface.php | 2 +- .../SalesRule/Api/Data/RuleLabelInterface.php | 2 +- .../Api/Data/RuleSearchResultInterface.php | 2 +- .../SalesRule/Api/RuleRepositoryInterface.php | 2 +- .../SalesRule/Block/Adminhtml/Promo/Quote.php | 2 +- .../Promo/Quote/Edit/DeleteButton.php | 2 +- .../Promo/Quote/Edit/GenericButton.php | 2 +- .../Promo/Quote/Edit/ResetButton.php | 2 +- .../Quote/Edit/SaveAndContinueButton.php | 2 +- .../Adminhtml/Promo/Quote/Edit/SaveButton.php | 2 +- .../Promo/Quote/Edit/Tab/Actions.php | 2 +- .../Promo/Quote/Edit/Tab/Conditions.php | 2 +- .../Promo/Quote/Edit/Tab/Coupons.php | 2 +- .../Promo/Quote/Edit/Tab/Coupons/Form.php | 2 +- .../Promo/Quote/Edit/Tab/Coupons/Grid.php | 2 +- .../Tab/Coupons/Grid/Column/Renderer/Used.php | 2 +- .../Adminhtml/Promo/Quote/Edit/Tab/Labels.php | 2 +- .../Block/Adminhtml/Promo/Widget/Chooser.php | 2 +- .../Magento/SalesRule/Block/Rss/Discounts.php | 2 +- .../Block/Widget/Form/Element/Dependence.php | 2 +- .../Controller/Adminhtml/Promo/Quote.php | 2 +- .../Adminhtml/Promo/Quote/ApplyRules.php | 2 +- .../Adminhtml/Promo/Quote/Chooser.php | 2 +- .../Adminhtml/Promo/Quote/CouponsGrid.php | 2 +- .../Promo/Quote/CouponsMassDelete.php | 2 +- .../Adminhtml/Promo/Quote/Delete.php | 2 +- .../Controller/Adminhtml/Promo/Quote/Edit.php | 2 +- .../Promo/Quote/ExportCouponsCsv.php | 2 +- .../Promo/Quote/ExportCouponsXml.php | 2 +- .../Adminhtml/Promo/Quote/Generate.php | 2 +- .../Adminhtml/Promo/Quote/Index.php | 2 +- .../Adminhtml/Promo/Quote/NewAction.php | 2 +- .../Adminhtml/Promo/Quote/NewActionHtml.php | 2 +- .../Promo/Quote/NewConditionHtml.php | 2 +- .../Controller/Adminhtml/Promo/Quote/Save.php | 9 +- .../Adminhtml/Promo/Widget/Chooser.php | 16 + .../Cron/AggregateSalesReportCouponsData.php | 2 +- app/code/Magento/SalesRule/Helper/Coupon.php | 2 +- .../SalesRule/Model/Converter/ToDataModel.php | 17 +- .../SalesRule/Model/Converter/ToModel.php | 2 +- app/code/Magento/SalesRule/Model/Coupon.php | 2 +- .../SalesRule/Model/Coupon/Codegenerator.php | 2 +- .../Model/Coupon/CodegeneratorInterface.php | 2 +- .../SalesRule/Model/Coupon/Massgenerator.php | 2 +- .../SalesRule/Model/CouponRepository.php | 10 +- .../SalesRule/Model/Data/Condition.php | 2 +- .../Model/Data/CouponGenerationSpec.php | 2 +- .../Model/Data/CouponMassDeleteResult.php | 2 +- .../Magento/SalesRule/Model/Data/Rule.php | 2 +- .../SalesRule/Model/Data/RuleLabel.php | 2 +- .../Plugin/QuoteConfigProductAttributes.php | 2 +- .../Model/Plugin/ResourceModel/Rule.php | 2 +- .../Magento/SalesRule/Model/Plugin/Rule.php | 2 +- .../SalesRule/Model/Quote/Discount.php | 2 +- .../SalesRule/Model/RegistryConstants.php | 2 +- .../SalesRule/Model/ResourceModel/Coupon.php | 2 +- .../Model/ResourceModel/Coupon/Collection.php | 2 +- .../Model/ResourceModel/Coupon/Usage.php | 2 +- .../Model/ResourceModel/ReadHandler.php | 2 +- .../Model/ResourceModel/Report/Collection.php | 2 +- .../Model/ResourceModel/Report/Rule.php | 2 +- .../ResourceModel/Report/Rule/Createdat.php | 2 +- .../ResourceModel/Report/Rule/Updatedat.php | 2 +- .../Report/Updatedat/Collection.php | 2 +- .../SalesRule/Model/ResourceModel/Rule.php | 75 +- .../Model/ResourceModel/Rule/Collection.php | 43 +- .../Model/ResourceModel/Rule/Customer.php | 2 +- .../Rule/Customer/Collection.php | 2 +- .../Model/ResourceModel/Rule/DateApplier.php | 2 +- .../ResourceModel/Rule/Quote/Collection.php | 2 +- .../Model/ResourceModel/SaveHandler.php | 2 +- .../Magento/SalesRule/Model/Rss/Discounts.php | 2 +- app/code/Magento/SalesRule/Model/Rule.php | 20 +- .../Model/Rule/Action/Collection.php | 2 +- .../Rule/Action/Discount/AbstractDiscount.php | 2 +- .../Model/Rule/Action/Discount/BuyXGetY.php | 2 +- .../Model/Rule/Action/Discount/ByFixed.php | 2 +- .../Model/Rule/Action/Discount/ByPercent.php | 2 +- .../Action/Discount/CalculatorFactory.php | 2 +- .../Model/Rule/Action/Discount/CartFixed.php | 2 +- .../Model/Rule/Action/Discount/Data.php | 2 +- .../Action/Discount/DiscountInterface.php | 2 +- .../Model/Rule/Action/Discount/ToFixed.php | 2 +- .../Model/Rule/Action/Discount/ToPercent.php | 2 +- .../SalesRule/Model/Rule/Action/Product.php | 2 +- .../Model/Rule/Condition/Address.php | 2 +- .../Model/Rule/Condition/Combine.php | 2 +- .../Model/Rule/Condition/Product.php | 24 +- .../Model/Rule/Condition/Product/Combine.php | 2 +- .../Model/Rule/Condition/Product/Found.php | 2 +- .../Rule/Condition/Product/Subselect.php | 2 +- .../Magento/SalesRule/Model/Rule/Customer.php | 2 +- .../SalesRule/Model/Rule/DataProvider.php | 2 +- .../Model/Rule/Metadata/ValueProvider.php | 2 +- .../SalesRule/Model/RuleRepository.php | 2 +- .../Magento/SalesRule/Model/RulesApplier.php | 2 +- .../Model/Service/CouponManagementService.php | 2 +- .../Model/Spi/CouponResourceInterface.php | 2 +- .../System/Config/Source/Coupon/Format.php | 2 +- app/code/Magento/SalesRule/Model/Utility.php | 4 +- .../Magento/SalesRule/Model/Validator.php | 2 +- .../SalesRule/Model/Validator/Pool.php | 2 +- .../AddSalesRuleNameToOrderObserver.php | 2 +- .../CatalogAttributeDeleteAfterObserver.php | 2 +- .../CatalogAttributeSaveAfterObserver.php | 2 +- .../Observer/CheckSalesRulesAvailability.php | 2 +- .../Observer/SalesOrderAfterPlaceObserver.php | 4 +- .../Magento/SalesRule/Setup/InstallData.php | 2 +- .../Magento/SalesRule/Setup/InstallSchema.php | 10 +- .../Magento/SalesRule/Setup/UpgradeData.php | 81 + .../Magento/SalesRule/Setup/UpgradeSchema.php | 2 +- .../Promo/Quote/Edit/DeleteButtonTest.php | 2 +- .../Promo/Quote/Edit/GenericButtonTest.php | 2 +- .../Promo/Quote/Edit/ResetButtonTest.php | 2 +- .../Quote/Edit/SaveAndContinueButtonTest.php | 2 +- .../Promo/Quote/Edit/SaveButtonTest.php | 2 +- .../Test/Unit/Block/Rss/DiscountsTest.php | 2 +- .../AggregateSalesReportCouponsDataTest.php | 2 +- .../SalesRule/Test/Unit/Helper/CouponTest.php | 2 +- .../Unit/Model/Converter/ToDataModelTest.php | 94 +- .../Test/Unit/Model/Converter/ToModelTest.php | 2 +- .../Unit/Model/Coupon/CodegeneratorTest.php | 2 +- .../Unit/Model/Coupon/MassgeneratorTest.php | 2 +- .../Test/Unit/Model/CouponRepositoryTest.php | 7 +- .../SalesRule/Test/Unit/Model/CouponTest.php | 2 +- .../QuoteConfigProductAttributesTest.php | 2 +- .../Model/Plugin/ResourceModel/RuleTest.php | 2 +- .../Test/Unit/Model/Plugin/RuleTest.php | 2 +- .../Test/Unit/Model/Quote/DiscountTest.php | 2 +- .../Model/ResourceModel/ReadHandlerTest.php | 2 +- .../ResourceModel/Report/CollectionTest.php | 2 +- .../Model/ResourceModel/Report/RuleTest.php | 2 +- .../ResourceModel/Rule/DateApplierTest.php | 2 +- .../Unit/Model/ResourceModel/RuleTest.php | 94 +- .../Model/ResourceModel/SaveHandlerTest.php | 2 +- .../Test/Unit/Model/Rss/DiscountsTest.php | 2 +- .../Rule/Action/Discount/ByPercentTest.php | 2 +- .../Rule/Action/Discount/CartFixedTest.php | 2 +- .../Rule/Action/Discount/ToPercentTest.php | 2 +- .../Unit/Model/Rule/Condition/ProductTest.php | 161 + .../Test/Unit/Model/Rule/DataProviderTest.php | 2 +- .../Model/Rule/Metadata/ValueProviderTest.php | 2 +- .../Model/Rule/Metadata/_files/MetaData.php | 2 +- .../Test/Unit/Model/RuleRepositoryTest.php | 2 +- .../SalesRule/Test/Unit/Model/RuleTest.php | 30 +- .../Test/Unit/Model/RulesApplierTest.php | 2 +- .../Service/CouponManagementServiceTest.php | 2 +- .../Config/Source/Coupon/FormatTest.php | 2 +- .../SalesRule/Test/Unit/Model/UtilityTest.php | 2 +- .../Test/Unit/Model/Validator/PoolTest.php | 2 +- .../Test/Unit/Model/ValidatorTest.php | 2 +- .../Model/_files/quote_item_downloadable.php | 2 +- .../Unit/Model/_files/quote_item_simple.php | 2 +- .../AddSalesRuleNameToOrderObserverTest.php | 2 +- ...atalogAttributeDeleteAfterObserverTest.php | 2 +- .../CatalogAttributeSaveAfterObserverTest.php | 2 +- .../SalesOrderAfterPlaceObserverTest.php | 14 +- app/code/Magento/SalesRule/composer.json | 2 +- app/code/Magento/SalesRule/etc/acl.xml | 2 +- .../Magento/SalesRule/etc/adminhtml/di.xml | 2 +- .../SalesRule/etc/adminhtml/events.xml | 2 +- .../Magento/SalesRule/etc/adminhtml/menu.xml | 2 +- .../SalesRule/etc/adminhtml/routes.xml | 2 +- .../SalesRule/etc/adminhtml/system.xml | 2 +- app/code/Magento/SalesRule/etc/config.xml | 2 +- app/code/Magento/SalesRule/etc/crontab.xml | 2 +- app/code/Magento/SalesRule/etc/di.xml | 4 +- app/code/Magento/SalesRule/etc/events.xml | 2 +- app/code/Magento/SalesRule/etc/fieldset.xml | 2 +- .../Magento/SalesRule/etc/frontend/di.xml | 2 +- app/code/Magento/SalesRule/etc/module.xml | 4 +- app/code/Magento/SalesRule/etc/sales.xml | 2 +- app/code/Magento/SalesRule/etc/webapi.xml | 2 +- app/code/Magento/SalesRule/registration.php | 2 +- .../sales_rule_promo_quote_couponsgrid.xml | 2 +- .../layout/sales_rule_promo_quote_edit.xml | 2 +- .../layout/sales_rule_promo_quote_index.xml | 2 +- .../templates/promo/salesrulejs.phtml | 2 +- .../adminhtml/templates/tab/coupons.phtml | 2 +- .../ui_component/sales_rule_form.xml | 2 +- .../base/web/js/form/element/coupon-type.js | 2 +- .../js/form/element/manage-coupon-codes.js | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 4 +- .../frontend/web/js/action/cancel-coupon.js | 96 +- .../frontend/web/js/action/set-coupon-code.js | 111 +- .../web/js/model/payment/discount-messages.js | 18 +- .../web/js/view/cart/totals/discount.js | 45 +- .../web/js/view/payment/discount-messages.js | 6 +- .../frontend/web/js/view/payment/discount.js | 120 +- .../frontend/web/js/view/summary/discount.js | 87 +- .../web/template/cart/totals/discount.html | 2 +- .../web/template/payment/discount.html | 4 +- .../web/template/summary/discount.html | 2 +- .../Magento/SalesSequence/Model/Builder.php | 2 +- .../Magento/SalesSequence/Model/Config.php | 2 +- .../SalesSequence/Model/EntityPool.php | 2 +- .../Magento/SalesSequence/Model/Manager.php | 2 +- app/code/Magento/SalesSequence/Model/Meta.php | 2 +- .../Magento/SalesSequence/Model/Profile.php | 2 +- .../Model/ResourceModel/Meta.php | 2 +- .../Model/ResourceModel/Profile.php | 2 +- .../Magento/SalesSequence/Model/Sequence.php | 2 +- .../Observer/SequenceCreatorObserver.php | 2 +- .../SalesSequence/Setup/InstallData.php | 2 +- .../SalesSequence/Setup/InstallSchema.php | 2 +- .../Test/Unit/Model/BuilderTest.php | 2 +- .../Test/Unit/Model/ManagerTest.php | 2 +- .../Unit/Model/ResourceModel/MetaTest.php | 5 +- .../Unit/Model/ResourceModel/ProfileTest.php | 4 +- .../Test/Unit/Model/SequenceTest.php | 2 +- app/code/Magento/SalesSequence/composer.json | 2 +- app/code/Magento/SalesSequence/etc/module.xml | 2 +- .../Magento/SalesSequence/registration.php | 2 +- .../Command/SampleDataDeployCommand.php | 2 +- .../Command/SampleDataRemoveCommand.php | 2 +- .../Command/SampleDataResetCommand.php | 2 +- .../SampleData/Console/CommandList.php | 2 +- .../Magento/SampleData/Model/Dependency.php | 6 +- .../Magento/SampleData/Setup/InstallData.php | 2 +- .../Command/SampleDataDeployCommandTest.php | 2 +- app/code/Magento/SampleData/cli_commands.php | 2 +- app/code/Magento/SampleData/composer.json | 2 +- app/code/Magento/SampleData/etc/di.xml | 2 +- app/code/Magento/SampleData/etc/module.xml | 5 +- app/code/Magento/SampleData/registration.php | 2 +- .../Adapter/Query/Preprocessor/Synonyms.php | 2 +- .../Search/Api/Data/SynonymGroupInterface.php | 2 +- .../Magento/Search/Api/SearchInterface.php | 2 +- .../Search/Api/SynonymAnalyzerInterface.php | 2 +- .../Api/SynonymGroupRepositoryInterface.php | 2 +- .../Search/Block/Adminhtml/Dashboard/Last.php | 2 +- .../Search/Block/Adminhtml/Dashboard/Top.php | 2 +- .../Search/Block/Adminhtml/Reports/Search.php | 2 +- .../Search/Block/Adminhtml/Synonyms.php | 2 +- .../Adminhtml/Synonyms/Edit/BackButton.php | 2 +- .../Adminhtml/Synonyms/Edit/DeleteButton.php | 2 +- .../Adminhtml/Synonyms/Edit/GenericButton.php | 2 +- .../Adminhtml/Synonyms/Edit/ResetButton.php | 2 +- .../Synonyms/Edit/SaveAndContinueButton.php | 2 +- .../Adminhtml/Synonyms/Edit/SaveButton.php | 2 +- .../Magento/Search/Block/Adminhtml/Term.php | 2 +- .../Search/Block/Adminhtml/Term/Edit.php | 2 +- .../Search/Block/Adminhtml/Term/Edit/Form.php | 2 +- app/code/Magento/Search/Block/Term.php | 2 +- .../Controller/Adminhtml/Synonyms/Delete.php | 2 +- .../Controller/Adminhtml/Synonyms/Edit.php | 2 +- .../Controller/Adminhtml/Synonyms/Index.php | 2 +- .../Adminhtml/Synonyms/MassDelete.php | 2 +- .../Adminhtml/Synonyms/NewAction.php | 2 +- .../Adminhtml/Synonyms/ResultPageBuilder.php | 2 +- .../Controller/Adminhtml/Synonyms/Save.php | 2 +- .../Search/Controller/Adminhtml/Term.php | 4 +- .../Controller/Adminhtml/Term/Delete.php | 2 +- .../Search/Controller/Adminhtml/Term/Edit.php | 2 +- .../Adminhtml/Term/ExportSearchCsv.php | 4 +- .../Adminhtml/Term/ExportSearchExcel.php | 2 +- .../Controller/Adminhtml/Term/Index.php | 2 +- .../Controller/Adminhtml/Term/MassDelete.php | 2 +- .../Controller/Adminhtml/Term/NewAction.php | 2 +- .../Controller/Adminhtml/Term/Report.php | 6 +- .../Search/Controller/Adminhtml/Term/Save.php | 2 +- .../Search/Controller/Ajax/Suggest.php | 2 +- .../Search/Controller/RegistryConstants.php | 2 +- .../Search/Controller/Term/Popular.php | 2 +- app/code/Magento/Search/Helper/Data.php | 2 +- .../Magento/Search/Model/AdapterFactory.php | 2 +- .../Adminhtml/System/Config/Source/Engine.php | 2 +- .../Magento/Search/Model/Autocomplete.php | 2 +- .../Autocomplete/DataProviderInterface.php | 2 +- .../Search/Model/Autocomplete/Item.php | 2 +- .../Search/Model/Autocomplete/ItemFactory.php | 2 +- .../Model/Autocomplete/ItemInterface.php | 2 +- .../Search/Model/AutocompleteInterface.php | 2 +- .../Magento/Search/Model/EngineResolver.php | 2 +- app/code/Magento/Search/Model/Query.php | 2 +- .../Magento/Search/Model/QueryFactory.php | 2 +- .../Search/Model/QueryFactoryInterface.php | 2 +- .../Magento/Search/Model/QueryInterface.php | 2 +- app/code/Magento/Search/Model/QueryResult.php | 2 +- .../Search/Model/ResourceModel/Query.php | 2 +- .../Model/ResourceModel/Query/Collection.php | 2 +- .../Model/ResourceModel/SynonymGroup.php | 2 +- .../ResourceModel/SynonymGroup/Collection.php | 2 +- .../Model/ResourceModel/SynonymReader.php | 2 +- app/code/Magento/Search/Model/Search.php | 2 +- .../Search/Model/SearchCollectionFactory.php | 2 +- .../Model/SearchCollectionInterface.php | 2 +- .../Magento/Search/Model/SearchEngine.php | 2 +- .../Search/Model/SearchEngine/Config.php | 2 +- .../Search/Model/SearchEngine/Config/Data.php | 15 +- .../Search/Model/SearchEngine/MenuBuilder.php | 2 +- .../Search/Model/Synonym/DataProvider.php | 2 +- .../Model/Synonym/MergeConflictException.php | 2 +- .../Magento/Search/Model/SynonymAnalyzer.php | 2 +- .../Magento/Search/Model/SynonymGroup.php | 2 +- .../Search/Model/SynonymGroupRepository.php | 2 +- .../Magento/Search/Model/SynonymReader.php | 2 +- .../Magento/Search/Setup/InstallSchema.php | 2 +- .../Magento/Search/Setup/UpgradeSchema.php | 2 +- .../Query/Preprocessor/SynonymsTest.php | 2 +- .../Controller/Adminhtml/Ajax/SuggestTest.php | 2 +- .../Adminhtml/Synonyms/DeleteTest.php | 2 +- .../Adminhtml/Term/ExportSearchCsvTest.php | 85 + .../Controller/Adminhtml/Term/IndexTest.php | 100 + .../Adminhtml/Term/MassDeleteTest.php | 2 +- .../Controller/Adminhtml/Term/ReportTest.php | 114 + .../Controller/Adminhtml/Term/SaveTest.php | 2 +- .../Search/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Model/AdapterFactoryTest.php | 2 +- .../Test/Unit/Model/AutocompleteTest.php | 2 +- .../Test/Unit/Model/EngineResolverTest.php | 2 +- .../Test/Unit/Model/QueryFactoryTest.php | 2 +- .../Test/Unit/Model/QueryResultTest.php | 2 +- .../Search/Test/Unit/Model/QueryTest.php | 2 +- .../Unit/Model/ResourceModel/QueryTest.php | 2 +- .../Model/ResourceModel/SynonymGroupTest.php | 2 +- .../Unit/Model/SearchEngine/ConfigTest.php | 2 +- .../Model/SearchEngine/MenuBuilderTest.php | 2 +- .../Test/Unit/Model/SearchEngineTest.php | 2 +- .../Unit/Model/SynonymGroupRepositoryTest.php | 2 +- .../Test/Unit/Model/SynonymGroupTest.php | 2 +- .../Listing/Column/Scope/Options.php | 2 +- .../Listing/Column/Store/Options.php | 2 +- .../Ui/Component/Listing/Column/StoreView.php | 2 +- .../Listing/Column/SynonymActions.php | 2 +- .../Ui/Component/Listing/Column/Website.php | 2 +- .../Listing/Column/Website/Options.php | 2 +- app/code/Magento/Search/composer.json | 2 +- app/code/Magento/Search/etc/acl.xml | 2 +- .../Magento/Search/etc/adminhtml/menu.xml | 2 +- .../Magento/Search/etc/adminhtml/routes.xml | 4 +- .../Magento/Search/etc/adminhtml/system.xml | 2 +- app/code/Magento/Search/etc/di.xml | 2 +- .../Search/etc/frontend/page_types.xml | 2 +- .../Magento/Search/etc/frontend/routes.xml | 4 +- app/code/Magento/Search/etc/module.xml | 2 +- app/code/Magento/Search/etc/search_engine.xml | 2 +- app/code/Magento/Search/etc/webapi.xml | 2 +- app/code/Magento/Search/registration.php | 2 +- .../layout/adminhtml_dashboard_index.xml | 2 +- .../adminhtml/layout/search_synonyms_edit.xml | 4 +- .../layout/search_synonyms_index.xml | 2 +- .../adminhtml/layout/search_synonyms_new.xml | 4 +- .../adminhtml/layout/search_term_block.xml | 2 +- .../adminhtml/layout/search_term_edit.xml | 2 +- .../layout/search_term_exportsearchcsv.xml | 2 +- .../layout/search_term_exportsearchexcel.xml | 2 +- .../layout/search_term_grid_block.xml | 2 +- .../adminhtml/layout/search_term_index.xml | 2 +- .../adminhtml/layout/search_term_report.xml | 2 +- .../layout/search_term_report_block.xml | 2 +- .../ui_component/search_synonyms_form.xml | 2 +- .../ui_component/search_synonyms_grid.xml | 2 +- .../Search/view/frontend/layout/default.xml | 2 +- .../frontend/layout/search_term_popular.xml | 2 +- .../Search/view/frontend/requirejs-config.js | 4 +- .../view/frontend/templates/form.mini.phtml | 2 +- .../Search/view/frontend/templates/term.phtml | 2 +- .../Search/view/frontend/web/form-mini.js | 45 +- .../Block/Adminhtml/Session/Activity.php | 2 +- .../Controller/Adminhtml/Session/Activity.php | 2 +- .../Adminhtml/Session/LogoutAll.php | 2 +- .../Security/Model/AdminSessionInfo.php | 2 +- .../Security/Model/AdminSessionsManager.php | 2 +- app/code/Magento/Security/Model/Config.php | 2 +- .../Model/Config/Source/ResetMethod.php | 2 +- .../Security/Model/ConfigInterface.php | 2 +- .../Model/PasswordResetRequestEvent.php | 2 +- .../Model/Plugin/AccountManagement.php | 14 +- .../Magento/Security/Model/Plugin/Auth.php | 2 +- .../Security/Model/Plugin/AuthSession.php | 2 +- .../Security/Model/Plugin/LoginController.php | 2 +- .../Model/ResourceModel/AdminSessionInfo.php | 2 +- .../AdminSessionInfo/Collection.php | 2 +- .../PasswordResetRequestEvent.php | 2 +- .../PasswordResetRequestEvent/Collection.php | 2 +- .../CollectionFactory.php | 2 +- .../Model/SecurityChecker/Frequency.php | 2 +- .../Model/SecurityChecker/Quantity.php | 2 +- .../SecurityCheckerInterface.php | 2 +- .../Magento/Security/Model/SecurityCookie.php | 2 +- .../Security/Model/SecurityManager.php | 2 +- .../Magento/Security/Setup/InstallSchema.php | 2 +- .../Magento/Security/Setup/UpgradeSchema.php | 2 +- .../Block/Adminhtml/Session/ActivityTest.php | 2 +- .../Adminhtml/Session/ActivityTest.php | 2 +- .../Adminhtml/Session/LogoutAllTest.php | 2 +- .../Test/Unit/Model/AdminSessionInfoTest.php | 2 +- .../Unit/Model/AdminSessionsManagerTest.php | 2 +- .../Model/Config/Source/ResetMethodTest.php | 2 +- .../Security/Test/Unit/Model/ConfigTest.php | 2 +- .../Model/Plugin/AccountManagementTest.php | 2 +- .../Unit/Model/Plugin/AuthSessionTest.php | 2 +- .../Test/Unit/Model/Plugin/AuthTest.php | 2 +- .../Unit/Model/Plugin/LoginControllerTest.php | 2 +- .../AdminSessionInfo/CollectionTest.php | 2 +- .../ResourceModel/AdminSessionInfoTest.php | 2 +- .../CollectionFactoryTest.php | 2 +- .../CollectionTest.php | 2 +- .../PasswordResetRequestEventTest.php | 2 +- .../Model/SecurityChecker/FrequencyTest.php | 2 +- .../Model/SecurityChecker/QuantityTest.php | 2 +- .../Test/Unit/Model/SecurityCookieTest.php | 2 +- .../Test/Unit/Model/SecurityManagerTest.php | 2 +- app/code/Magento/Security/composer.json | 2 +- .../Magento/Security/etc/adminhtml/di.xml | 7 +- .../Magento/Security/etc/adminhtml/routes.xml | 2 +- .../Magento/Security/etc/adminhtml/system.xml | 2 +- app/code/Magento/Security/etc/config.xml | 2 +- app/code/Magento/Security/etc/crontab.xml | 2 +- app/code/Magento/Security/etc/di.xml | 2 +- app/code/Magento/Security/etc/module.xml | 2 +- app/code/Magento/Security/registration.php | 2 +- .../view/adminhtml/layout/default.xml | 2 +- .../layout/security_session_activity.xml | 2 +- .../adminhtml/page_layout/admin-popup.xml | 2 +- .../view/adminhtml/requirejs-config.js | 4 +- .../templates/page/activity_link.phtml | 2 +- .../templates/session/activity.phtml | 2 +- .../view/adminhtml/web/css/activity.css | 2 +- .../view/adminhtml/web/js/confirm-redirect.js | 2 +- .../Block/Plugin/Catalog/Product/View.php | 2 +- app/code/Magento/SendFriend/Block/Send.php | 2 +- .../Magento/SendFriend/Controller/Product.php | 2 +- .../SendFriend/Controller/Product/Send.php | 2 +- .../Controller/Product/Sendmail.php | 2 +- app/code/Magento/SendFriend/Helper/Data.php | 2 +- .../Model/ResourceModel/SendFriend.php | 2 +- .../ResourceModel/SendFriend/Collection.php | 2 +- .../Magento/SendFriend/Model/SendFriend.php | 2 +- .../SendFriend/Model/Source/Checktype.php | 2 +- .../SendFriend/Setup/InstallSchema.php | 2 +- .../Block/Plugin/Catalog/Product/ViewTest.php | 2 +- .../SendFriend/Test/Unit/Block/SendTest.php | 2 +- .../Test/Unit/Controller/Product/SendTest.php | 2 +- .../Unit/Controller/Product/SendmailTest.php | 2 +- .../Test/Unit/Model/SendFriendTest.php | 2 +- app/code/Magento/SendFriend/composer.json | 2 +- .../SendFriend/etc/adminhtml/system.xml | 2 +- app/code/Magento/SendFriend/etc/config.xml | 2 +- .../SendFriend/etc/email_templates.xml | 2 +- .../Magento/SendFriend/etc/frontend/di.xml | 2 +- .../SendFriend/etc/frontend/page_types.xml | 2 +- .../SendFriend/etc/frontend/routes.xml | 4 +- app/code/Magento/SendFriend/etc/module.xml | 2 +- app/code/Magento/SendFriend/registration.php | 2 +- .../view/frontend/email/product_share.html | 2 +- .../layout/sendfriend_product_send.xml | 2 +- .../view/frontend/templates/send.phtml | 2 +- .../view/frontend/web/back-event.js | 6 +- .../Shipping/Block/Adminhtml/Create.php | 2 +- .../Shipping/Block/Adminhtml/Create/Form.php | 2 +- .../Shipping/Block/Adminhtml/Create/Items.php | 2 +- .../Block/Adminhtml/Order/Packaging.php | 2 +- .../Block/Adminhtml/Order/Packaging/Grid.php | 2 +- .../Block/Adminhtml/Order/Tracking.php | 2 +- .../Adminhtml/Order/Tracking/Invoice.php | 2 +- .../Block/Adminhtml/Order/Tracking/View.php | 2 +- .../Magento/Shipping/Block/Adminhtml/View.php | 2 +- .../Block/Adminhtml/View/Comments.php | 2 +- .../Shipping/Block/Adminhtml/View/Form.php | 2 +- .../Shipping/Block/Adminhtml/View/Items.php | 2 +- app/code/Magento/Shipping/Block/Items.php | 2 +- .../Magento/Shipping/Block/Order/Shipment.php | 2 +- .../Magento/Shipping/Block/Tracking/Ajax.php | 2 +- .../Magento/Shipping/Block/Tracking/Link.php | 2 +- .../Magento/Shipping/Block/Tracking/Popup.php | 2 +- .../Adminhtml/Order/Shipment/AddComment.php | 2 +- .../Adminhtml/Order/Shipment/AddTrack.php | 2 +- .../Adminhtml/Order/Shipment/CreateLabel.php | 2 +- .../Adminhtml/Order/Shipment/Email.php | 2 +- .../Order/Shipment/GetShippingItemsGrid.php | 2 +- .../Adminhtml/Order/Shipment/Index.php | 2 +- .../Order/Shipment/MassPrintShippingLabel.php | 2 +- .../Adminhtml/Order/Shipment/NewAction.php | 2 +- .../Adminhtml/Order/Shipment/Pdfshipments.php | 2 +- .../Adminhtml/Order/Shipment/PrintAction.php | 2 +- .../Adminhtml/Order/Shipment/PrintLabel.php | 2 +- .../Adminhtml/Order/Shipment/PrintPackage.php | 2 +- .../Adminhtml/Order/Shipment/RemoveTrack.php | 2 +- .../Adminhtml/Order/Shipment/Save.php | 13 +- .../Adminhtml/Order/Shipment/Start.php | 2 +- .../Adminhtml/Order/Shipment/View.php | 2 +- .../Adminhtml/Order/ShipmentLoader.php | 2 +- .../Shipment/MassPrintShippingLabel.php | 2 +- .../Shipping/Controller/Tracking/Popup.php | 2 +- app/code/Magento/Shipping/Helper/Carrier.php | 2 +- app/code/Magento/Shipping/Helper/Data.php | 2 +- .../Model/Carrier/AbstractCarrier.php | 2 +- .../Carrier/AbstractCarrierInterface.php | 2 +- .../Model/Carrier/AbstractCarrierOnline.php | 2 +- .../Model/Carrier/CarrierInterface.php | 2 +- .../Model/Carrier/Source/GenericDefault.php | 2 +- .../Model/Carrier/Source/GenericInterface.php | 2 +- .../Magento/Shipping/Model/CarrierFactory.php | 2 +- .../Model/CarrierFactoryInterface.php | 2 +- app/code/Magento/Shipping/Model/Config.php | 2 +- .../Model/Config/Source/Allmethods.php | 2 +- .../Config/Source/Allspecificcountries.php | 2 +- .../Model/Config/Source/Online/Mode.php | 2 +- .../Config/Source/Online/Requesttype.php | 2 +- app/code/Magento/Shipping/Model/Info.php | 2 +- app/code/Magento/Shipping/Model/Observer.php | 2 +- .../Shipping/Model/Order/Pdf/Packaging.php | 2 +- .../Magento/Shipping/Model/Order/Track.php | 2 +- .../Magento/Shipping/Model/Rate/Result.php | 2 +- .../ResourceModel/Order/Track/Collection.php | 2 +- .../Shipping/Model/Shipment/Request.php | 2 +- .../Model/Shipment/ReturnShipment.php | 2 +- .../Shipping/Model/ShipmentNotifier.php | 2 +- app/code/Magento/Shipping/Model/Shipping.php | 31 +- .../Model/Shipping/LabelGenerator.php | 2 +- .../Shipping/Model/Shipping/Labels.php | 2 +- .../Shipping/Model/Simplexml/Element.php | 2 +- .../Shipping/Model/Source/HandlingAction.php | 2 +- .../Shipping/Model/Source/HandlingType.php | 2 +- .../Shipping/Model/Tracking/Result.php | 2 +- .../Model/Tracking/Result/AbstractResult.php | 2 +- .../Shipping/Model/Tracking/Result/Error.php | 2 +- .../Shipping/Model/Tracking/Result/Status.php | 2 +- .../Block/Adminhtml/Order/TrackingTest.php | 2 +- .../Order/Shipment/AddCommentTest.php | 2 +- .../Adminhtml/Order/Shipment/AddTrackTest.php | 2 +- .../Order/Shipment/CreateLabelTest.php | 2 +- .../Adminhtml/Order/Shipment/EmailTest.php | 2 +- .../Shipment/GetShippingItemsGridTest.php | 2 +- .../Order/Shipment/NewActionTest.php | 2 +- .../Order/Shipment/PrintLabelTest.php | 2 +- .../Order/Shipment/PrintPackageTest.php | 2 +- .../Order/Shipment/RemoveTrackTest.php | 2 +- .../Adminhtml/Order/Shipment/SaveTest.php | 18 +- .../Adminhtml/Order/Shipment/ViewTest.php | 2 +- .../Adminhtml/Order/ShipmentLoaderTest.php | 2 +- .../Shipping/Test/Unit/Helper/CarrierTest.php | 2 +- .../Carrier/AbstractCarrierOnlineTest.php | 2 +- .../Test/Unit/Model/Order/TrackTest.php | 2 +- .../Test/Unit/Model/ShipmentNotifierTest.php | 2 +- .../Shipping/Test/Unit/Model/ShipmentTest.php | 2 +- .../Model/Shipping/LabelGeneratorTest.php | 2 +- .../Test/Unit/Model/Shipping/LabelsTest.php | 2 +- .../Shipping/Test/Unit/Model/ShippingTest.php | 2 +- .../Test/Unit/Model/Simplexml/ElementTest.php | 2 +- app/code/Magento/Shipping/composer.json | 5 +- app/code/Magento/Shipping/etc/acl.xml | 2 +- .../Magento/Shipping/etc/adminhtml/di.xml | 2 +- .../Magento/Shipping/etc/adminhtml/routes.xml | 2 +- .../Magento/Shipping/etc/adminhtml/system.xml | 2 +- app/code/Magento/Shipping/etc/config.xml | 2 +- app/code/Magento/Shipping/etc/crontab.xml | 2 +- app/code/Magento/Shipping/etc/di.xml | 14 +- .../Shipping/etc/frontend/page_types.xml | 2 +- .../Magento/Shipping/etc/frontend/routes.xml | 4 +- app/code/Magento/Shipping/etc/module.xml | 2 +- app/code/Magento/Shipping/registration.php | 2 +- .../adminhtml_order_shipment_addcomment.xml | 2 +- .../adminhtml_order_shipment_addtrack.xml | 2 +- .../layout/adminhtml_order_shipment_new.xml | 4 +- .../adminhtml_order_shipment_removetrack.xml | 2 +- .../layout/adminhtml_order_shipment_view.xml | 4 +- .../layout/sales_order_invoice_new.xml | 2 +- .../adminhtml/layout/sales_order_view.xml | 2 +- .../adminhtml/templates/create/form.phtml | 2 +- .../adminhtml/templates/create/items.phtml | 2 +- .../create/items/renderer/default.phtml | 2 +- .../templates/order/packaging/grid.phtml | 2 +- .../templates/order/packaging/packed.phtml | 2 +- .../templates/order/packaging/popup.phtml | 2 +- .../order/packaging/popup_content.phtml | 2 +- .../adminhtml/templates/order/tracking.phtml | 2 +- .../templates/order/tracking/view.phtml | 2 +- .../adminhtml/templates/order/view/info.phtml | 2 +- .../view/adminhtml/templates/view/form.phtml | 2 +- .../view/adminhtml/templates/view/items.phtml | 2 +- .../view/items/renderer/default.phtml | 2 +- .../view/adminhtml/web/js/packages.js | 2 +- .../view/adminhtml/web/order/packaging.js | 17 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../frontend/layout/sales_guest_reorder.xml | 2 +- .../frontend/layout/sales_guest_shipment.xml | 2 +- .../view/frontend/layout/sales_guest_view.xml | 2 +- .../frontend/layout/sales_order_reorder.xml | 2 +- .../frontend/layout/sales_order_shipment.xml | 2 +- .../view/frontend/layout/sales_order_view.xml | 2 +- .../layout/shipping_tracking_popup.xml | 2 +- .../view/frontend/templates/items.phtml | 2 +- .../frontend/templates/order/shipment.phtml | 2 +- .../frontend/templates/tracking/details.phtml | 2 +- .../frontend/templates/tracking/link.phtml | 2 +- .../frontend/templates/tracking/popup.phtml | 2 +- .../templates/tracking/progress.phtml | 4 +- .../view/frontend/web/js/model/config.js | 17 +- .../view/checkout/shipping/shipping-policy.js | 3 +- .../checkout/shipping/shipping-policy.html | 2 +- .../Magento/Sitemap/Block/Adminhtml/Edit.php | 2 +- .../Sitemap/Block/Adminhtml/Edit/Form.php | 2 +- .../Block/Adminhtml/Grid/Renderer/Action.php | 2 +- .../Block/Adminhtml/Grid/Renderer/Link.php | 2 +- .../Block/Adminhtml/Grid/Renderer/Time.php | 2 +- .../Sitemap/Block/Adminhtml/Sitemap.php | 2 +- .../Sitemap/Controller/Adminhtml/Sitemap.php | 2 +- .../Controller/Adminhtml/Sitemap/Delete.php | 2 +- .../Controller/Adminhtml/Sitemap/Edit.php | 2 +- .../Controller/Adminhtml/Sitemap/Generate.php | 2 +- .../Controller/Adminhtml/Sitemap/Index.php | 2 +- .../Adminhtml/Sitemap/NewAction.php | 2 +- .../Controller/Adminhtml/Sitemap/Save.php | 2 +- app/code/Magento/Sitemap/Helper/Data.php | 2 +- .../Sitemap/Model/Config/Backend/Priority.php | 2 +- .../Sitemap/Model/Config/Source/Frequency.php | 2 +- app/code/Magento/Sitemap/Model/Observer.php | 2 +- .../Model/ResourceModel/Catalog/Category.php | 2 +- .../Model/ResourceModel/Catalog/Product.php | 2 +- .../Sitemap/Model/ResourceModel/Cms/Page.php | 2 +- .../Sitemap/Model/ResourceModel/Sitemap.php | 2 +- .../ResourceModel/Sitemap/Collection.php | 2 +- app/code/Magento/Sitemap/Model/Sitemap.php | 6 +- .../Source/Product/Image/IncludeImage.php | 2 +- .../Magento/Sitemap/Setup/InstallSchema.php | 2 +- .../Adminhtml/Sitemap/DeleteTest.php | 2 +- .../Controller/Adminhtml/Sitemap/SaveTest.php | 2 +- .../Sitemap/Test/Unit/Helper/DataTest.php | 2 +- .../Sitemap/Test/Unit/Model/ObserverTest.php | 2 +- .../Sitemap/Test/Unit/Model/SitemapTest.php | 28 +- .../Test/Unit/Model/_files/sitemap-1-1.xml | 2 +- .../Test/Unit/Model/_files/sitemap-1-2.xml | 2 +- .../Test/Unit/Model/_files/sitemap-1-3.xml | 2 +- .../Test/Unit/Model/_files/sitemap-1-4.xml | 2 +- .../Test/Unit/Model/_files/sitemap-index.xml | 2 +- .../Test/Unit/Model/_files/sitemap-single.xml | 2 +- app/code/Magento/Sitemap/composer.json | 5 +- app/code/Magento/Sitemap/etc/acl.xml | 2 +- .../Magento/Sitemap/etc/adminhtml/menu.xml | 2 +- .../Magento/Sitemap/etc/adminhtml/routes.xml | 2 +- .../Magento/Sitemap/etc/adminhtml/system.xml | 2 +- app/code/Magento/Sitemap/etc/config.xml | 2 +- app/code/Magento/Sitemap/etc/crontab.xml | 2 +- app/code/Magento/Sitemap/etc/di.xml | 9 +- .../Magento/Sitemap/etc/email_templates.xml | 2 +- app/code/Magento/Sitemap/etc/module.xml | 2 +- app/code/Magento/Sitemap/registration.php | 2 +- .../adminhtml/email/generate_warnings.html | 2 +- .../layout/adminhtml_sitemap_index.xml | 2 +- .../adminhtml_sitemap_index_grid_block.xml | 2 +- .../Magento/Store/Api/Data/GroupInterface.php | 2 +- .../Store/Api/Data/StoreConfigInterface.php | 2 +- .../Magento/Store/Api/Data/StoreInterface.php | 2 +- .../Store/Api/Data/WebsiteInterface.php | 2 +- .../Store/Api/GroupRepositoryInterface.php | 2 +- .../Store/Api/StoreConfigManagerInterface.php | 2 +- .../Store/Api/StoreCookieManagerInterface.php | 2 +- .../Store/Api/StoreManagementInterface.php | 2 +- .../Store/Api/StoreRepositoryInterface.php | 2 +- .../Store/Api/StoreResolverInterface.php | 2 +- .../Api/StoreWebsiteRelationInterface.php | 2 +- .../Store/Api/WebsiteManagementInterface.php | 2 +- .../Store/Api/WebsiteRepositoryInterface.php | 2 +- .../Store/App/Action/Plugin/Context.php | 2 +- .../Store/App/Action/Plugin/StoreCheck.php | 2 +- .../App/Config/Source/RuntimeConfigSource.php | 197 + .../Magento/Store/App/Config/Type/Scopes.php | 62 + .../FrontController/Plugin/DefaultStore.php | 2 +- .../Plugin/RequestPreprocessor.php | 2 +- .../Store/App/Request/PathInfoProcessor.php | 2 +- .../Magento/Store/App/Response/Redirect.php | 2 +- .../Magento/Store/Block/Store/Switcher.php | 2 +- app/code/Magento/Store/Block/Switcher.php | 2 +- .../Console/Command/StoreListCommand.php | 74 + .../Console/Command/WebsiteListCommand.php | 73 + .../Store/Controller/Store/SwitchAction.php | 2 +- .../Magento/Store/Model/Address/Renderer.php | 2 +- .../Magento/Store/Model/App/Emulation.php | 2 +- .../Magento/Store/Model/BaseUrlChecker.php | 2 +- .../Magento/Store/Model/Config/Converter.php | 21 +- .../Store/Model/Config/Placeholder.php | 165 + .../Store/Model/Config/Processor/Fallback.php | 115 + .../Model/Config/Processor/Placeholder.php | 163 +- .../Model/Config/Reader/DefaultReader.php | 73 - .../Store/Model/Config/Reader/ReaderPool.php | 36 - .../Reader/Source/Dynamic/DefaultScope.php | 61 + .../Config/Reader/Source/Dynamic/Store.php | 92 + .../Config/Reader/Source/Dynamic/Website.php | 80 + .../Reader/Source/Initial/DefaultScope.php | 50 + .../Config/Reader/Source/Initial/Store.php | 75 + .../Config/Reader/Source/Initial/Website.php | 60 + .../Store/Model/Config/Reader/Store.php | 84 - .../Store/Model/Config/Reader/Website.php | 88 - .../Magento/Store/Model/Config/StoreView.php | 2 +- .../Magento/Store/Model/Data/StoreConfig.php | 2 +- .../Store/Model/DefaultStoreScopeProvider.php | 2 +- app/code/Magento/Store/Model/Group.php | 4 +- .../Magento/Store/Model/GroupRepository.php | 57 +- .../Store/Model/HeaderProvider/Hsts.php | 2 +- .../Model/HeaderProvider/UpgradeInsecure.php | 2 +- app/code/Magento/Store/Model/Information.php | 2 +- app/code/Magento/Store/Model/PathConfig.php | 2 +- .../Store/Model/Plugin/StoreCookie.php | 2 +- .../Magento/Store/Model/Resolver/Group.php | 2 +- .../Magento/Store/Model/Resolver/Store.php | 2 +- .../Magento/Store/Model/Resolver/Website.php | 2 +- .../Config/Collection/Scoped.php | 2 +- .../Store/Model/ResourceModel/Group.php | 2 +- .../Model/ResourceModel/Group/Collection.php | 2 +- .../Store/Model/ResourceModel/Store.php | 2 +- .../Model/ResourceModel/Store/Collection.php | 2 +- .../ResourceModel/StoreWebsiteRelation.php | 2 +- .../Store/Model/ResourceModel/Website.php | 2 +- .../ResourceModel/Website/Collection.php | 2 +- .../ResourceModel/Website/Grid/Collection.php | 2 +- .../Store/Model/ScopeFallbackResolver.php | 2 +- .../Magento/Store/Model/ScopeInterface.php | 2 +- .../Magento/Store/Model/ScopeTreeProvider.php | 2 +- .../Magento/Store/Model/ScopeValidator.php | 2 +- .../Model/Service/StoreConfigManager.php | 2 +- app/code/Magento/Store/Model/Store.php | 16 +- .../Store/Model/StoreCookieManager.php | 2 +- .../Store/Model/StoreIsInactiveException.php | 2 +- .../Magento/Store/Model/StoreManagement.php | 2 +- app/code/Magento/Store/Model/StoreManager.php | 14 +- .../Store/Model/StoreManagerInterface.php | 2 +- .../Magento/Store/Model/StoreRepository.php | 72 +- .../Magento/Store/Model/StoreResolver.php | 51 +- .../Store/Model/StoreResolver/Group.php | 2 +- .../Model/StoreResolver/ReaderInterface.php | 2 +- .../Store/Model/StoreResolver/ReaderList.php | 2 +- .../Store/Model/StoreResolver/Store.php | 2 +- .../Store/Model/StoreResolver/Website.php | 2 +- .../Store/Model/StoreScopeProvider.php | 2 +- app/code/Magento/Store/Model/StoresConfig.php | 2 +- app/code/Magento/Store/Model/System/Store.php | 2 +- app/code/Magento/Store/Model/Website.php | 4 +- .../Magento/Store/Model/WebsiteManagement.php | 2 +- .../Magento/Store/Model/WebsiteRepository.php | 104 +- .../Magento/Store/Setup/InstallSchema.php | 2 +- .../Unit/App/Action/Plugin/ContextTest.php | 2 +- .../Unit/App/Action/Plugin/StoreCheckTest.php | 2 +- .../Config/Source/RuntimeConfigSourceTest.php | 358 + .../Plugin/RequestPreprocessorTest.php | 2 +- .../App/Request/PathInfoProcessorTest.php | 2 +- .../Test/Unit/App/Response/RedirectTest.php | 2 +- .../Test/Unit/Block/Store/SwitcherTest.php | 2 +- .../Store/Test/Unit/Block/SwitcherTest.php | 2 +- .../Console/Command/StoreListCommandTest.php | 117 + .../Command/WebsiteListCommandTest.php | 115 + .../Controller/Store/SwitchActionTest.php | 2 +- .../Test/Unit/Model/Address/RendererTest.php | 2 +- .../Test/Unit/Model/App/EmulationTest.php | 2 +- .../Test/Unit/Model/Config/ConverterTest.php | 27 +- .../Unit/Model/Config/PlaceholderTest.php | 64 + .../Config/Processor/PlaceholderTest.php | 73 +- .../Model/Config/Reader/DefaultReaderTest.php | 74 - .../Model/Config/Reader/ReaderPoolTest.php | 83 - .../Source/Dynamic/DefaultScopeTest.php | 46 + .../Reader/Source/Dynamic/StoreTest.php | 144 + .../Reader/Source/Dynamic/WebsiteTest.php | 113 + .../Source/Initial/DefaultScopeTest.php | 34 + .../Reader/Source/Initial/StoreTest.php | 81 + .../Reader/Source/Initial/WebsiteTest.php | 62 + .../Unit/Model/Config/Reader/StoreTest.php | 159 - .../Unit/Model/Config/Reader/WebsiteTest.php | 131 - .../Unit/Model/HeaderProvider/HstsTest.php | 2 +- .../HeaderProvider/UpgradeInsecureTest.php | 2 +- .../Store/Test/Unit/Model/InformationTest.php | 2 +- .../Store/Test/Unit/Model/PathConfigTest.php | 2 +- .../Unit/Model/Plugin/StoreCookieTest.php | 2 +- .../Test/Unit/Model/Resolver/GroupTest.php | 2 +- .../Test/Unit/Model/Resolver/StoreTest.php | 2 +- .../Test/Unit/Model/Resolver/WebsiteTest.php | 2 +- .../StoreWebsiteRelationTest.php | 2 +- .../Unit/Model/ScopeFallbackResolverTest.php | 2 +- .../Test/Unit/Model/ScopeTreeProviderTest.php | 2 +- .../Test/Unit/Model/ScopeValidatorTest.php | 2 +- .../Model/Service/StoreConfigManagerTest.php | 2 +- .../Test/Unit/Model/StoreManagementTest.php | 2 +- .../Test/Unit/Model/StoreManagerTest.php | 4 +- .../Test/Unit/Model/StoreRepositoryTest.php | 185 + .../Store/Test/Unit/Model/StoreTest.php | 2 +- .../Test/Unit/Model/StoresConfigTest.php | 2 +- .../Test/Unit/Model/System/StoreTest.php | 2 +- .../Test/Unit/Model/WebsiteManagementTest.php | 2 +- .../Test/Unit/Model/WebsiteRepositoryTest.php | 93 +- .../Store/Test/Unit/Model/WebsiteTest.php | 2 +- .../Ui/Component/Listing/Column/StoreTest.php | 2 +- .../Url/Plugin/RouteParamsResolverTest.php | 2 +- .../Test/Unit/Url/Plugin/SecurityInfoTest.php | 2 +- .../Ui/Component/Form/Fieldset/Websites.php | 2 +- .../Ui/Component/Listing/Column/Store.php | 2 +- .../Listing/Column/Store/Options.php | 2 +- .../Store/Url/Plugin/RouteParamsResolver.php | 2 +- .../Magento/Store/Url/Plugin/SecurityInfo.php | 2 +- app/code/Magento/Store/composer.json | 5 +- app/code/Magento/Store/etc/adminhtml/di.xml | 2 +- app/code/Magento/Store/etc/cache.xml | 2 +- app/code/Magento/Store/etc/config.xml | 2 +- app/code/Magento/Store/etc/config.xsd | 2 +- .../Magento/Store/etc/data_source/website.xml | 4 +- app/code/Magento/Store/etc/di.xml | 99 +- app/code/Magento/Store/etc/frontend/di.xml | 2 +- .../Magento/Store/etc/frontend/routes.xml | 2 +- .../Magento/Store/etc/frontend/sections.xml | 2 +- app/code/Magento/Store/etc/module.xml | 2 +- app/code/Magento/Store/etc/webapi.xml | 2 +- app/code/Magento/Store/registration.php | 2 +- .../frontend/templates/switch/flags.phtml | 2 +- .../frontend/templates/switch/languages.phtml | 2 +- .../frontend/templates/switch/stores.phtml | 2 +- .../Swagger/Controller/Index/Index.php | 2 +- .../Test/Unit/Controller/Index/IndexTest.php | 2 +- app/code/Magento/Swagger/composer.json | 2 +- .../Magento/Swagger/etc/frontend/routes.xml | 2 +- app/code/Magento/Swagger/etc/module.xml | 2 +- app/code/Magento/Swagger/registration.php | 2 +- .../frontend/layout/swagger_index_index.xml | 2 +- .../web/swagger-ui/js/magento-swagger.js | 3 +- .../Attribute/Edit/Options/AbstractSwatch.php | 2 +- .../Adminhtml/Attribute/Edit/Options/Text.php | 2 +- .../Attribute/Edit/Options/Visual.php | 2 +- .../Adminhtml/Product/Attribute/Edit/Form.php | 36 +- .../Block/LayeredNavigation/RenderLayered.php | 2 +- .../Block/Product/Renderer/Configurable.php | 2 +- .../Product/Renderer/Listing/Configurable.php | 2 +- .../Controller/Adminhtml/Iframe/Show.php | 2 +- .../Product/Attribute/Plugin/Save.php | 2 +- .../Swatches/Controller/Ajax/Media.php | 2 +- app/code/Magento/Swatches/Helper/Data.php | 60 +- app/code/Magento/Swatches/Helper/Media.php | 2 +- .../Magento/Swatches/Model/AttributesList.php | 2 +- .../Model/Form/Element/AbstractSwatch.php | 2 +- .../Model/Form/Element/SwatchText.php | 2 +- .../Model/Form/Element/SwatchVisual.php | 2 +- .../Swatches/Model/Plugin/Configurable.php | 2 +- .../Swatches/Model/Plugin/EavAttribute.php | 35 +- .../Swatches/Model/Plugin/FilterRenderer.php | 2 +- .../Magento/Swatches/Model/Plugin/Product.php | 2 +- .../Swatches/Model/Plugin/ProductImage.php | 2 +- .../Swatches/Model/ResourceModel/Swatch.php | 2 +- .../Model/ResourceModel/Swatch/Collection.php | 2 +- app/code/Magento/Swatches/Model/Swatch.php | 2 +- .../Observer/AddFieldsToAttributeObserver.php | 2 +- .../AddSwatchAttributeTypeObserver.php | 2 +- .../Plugin/Catalog/CacheInvalidate.php | 2 +- .../Magento/Swatches/Setup/InstallData.php | 2 +- .../Magento/Swatches/Setup/InstallSchema.php | 2 +- .../Magento/Swatches/Setup/UpgradeData.php | 47 +- .../Edit/Options/AbstractSwatchTest.php | 2 +- .../Product/Attribute/Edit/FormTest.php | 4 +- .../LayeredNavigation/RenderLayeredTest.php | 2 +- .../Product/Renderer/ConfigurableTest.php | 2 +- .../Renderer/Listing/ConfigurableTest.php | 2 +- .../Controller/Adminhtml/Iframe/ShowTest.php | 2 +- .../Product/Attribute/Plugin/SaveTest.php | 2 +- .../Test/Unit/Controller/Ajax/MediaTest.php | 2 +- .../Swatches/Test/Unit/Helper/DataTest.php | 62 +- .../Swatches/Test/Unit/Helper/MediaTest.php | 2 +- .../Test/Unit/Model/AttributesListTest.php | 2 +- .../Model/Form/Element/AbstractSwatchTest.php | 2 +- .../Unit/Model/Plugin/ConfigurableTest.php | 2 +- .../Unit/Model/Plugin/EavAttributeTest.php | 34 +- .../Unit/Model/Plugin/FilterRendererTest.php | 2 +- .../Unit/Model/Plugin/ProductImageTest.php | 2 +- .../Test/Unit/Model/Plugin/ProductTest.php | 2 +- .../AddFieldsToAttributeObserverTest.php | 2 +- .../AddSwatchAttributeTypeObserverTest.php | 2 +- .../Plugin/Catalog/CacheInvalidateTest.php | 2 +- app/code/Magento/Swatches/composer.json | 2 +- .../Magento/Swatches/etc/adminhtml/di.xml | 2 +- .../Magento/Swatches/etc/adminhtml/events.xml | 2 +- .../Magento/Swatches/etc/adminhtml/routes.xml | 2 +- .../Magento/Swatches/etc/adminhtml/system.xml | 2 +- app/code/Magento/Swatches/etc/config.xml | 2 +- app/code/Magento/Swatches/etc/di.xml | 2 +- .../Magento/Swatches/etc/frontend/routes.xml | 4 +- app/code/Magento/Swatches/etc/module.xml | 4 +- app/code/Magento/Swatches/etc/view.xml | 2 +- app/code/Magento/Swatches/registration.php | 2 +- .../layout/catalog_product_attribute_edit.xml | 4 +- .../catalog_product_attribute_edit_popup.xml | 2 +- .../adminhtml/layout/catalog_product_form.xml | 2 +- .../catalog_product_superconfig_config.xml | 2 +- .../view/adminhtml/requirejs-config.js | 2 +- .../catalog/product/attribute/js.phtml | 2 +- .../catalog/product/attribute/text.phtml | 2 +- .../catalog/product/attribute/visual.phtml | 11 +- .../attribute/steps/attributes_values.phtml | 2 +- .../ui_component/design_config_form.xml | 2 +- .../product_attribute_add_form.xml | 2 +- .../view/adminhtml/web/css/swatches.css | 2 +- .../web/js/form/element/swatch-visual.js | 2 +- .../adminhtml/web/js/product-attributes.js | 2 +- .../Swatches/view/adminhtml/web/js/text.js | 2 +- .../view/adminhtml/web/js/type-change.js | 5 +- .../Swatches/view/adminhtml/web/js/visual.js | 2 +- .../adminhtml/web/template/swatch-visual.html | 2 +- .../frontend/layout/catalog_category_view.xml | 2 +- ...catalog_product_view_type_configurable.xml | 2 +- .../layout/catalogsearch_advanced_result.xml | 2 +- .../layout/catalogsearch_result_index.xml | 2 +- ...ckout_cart_configure_type_configurable.xml | 2 +- ...list_index_configure_type_configurable.xml | 2 +- .../templates/product/layered/renderer.phtml | 2 +- .../templates/product/listing/renderer.phtml | 24 +- .../templates/product/view/renderer.phtml | 6 +- .../view/frontend/web/css/swatches.css | 2 +- .../frontend/web/js/catalog-add-to-cart.js | 17 + .../view/frontend/web/js/swatch-renderer.js | 118 +- .../SwatchesLayeredNavigation/composer.json | 2 +- .../SwatchesLayeredNavigation/etc/module.xml | 2 +- .../registration.php | 2 +- .../product_attribute_add_form.xml | 2 +- .../Tax/Api/Data/AppliedTaxInterface.php | 2 +- .../Tax/Api/Data/AppliedTaxRateInterface.php | 2 +- .../Api/Data/GrandTotalDetailsInterface.php | 2 +- .../Tax/Api/Data/GrandTotalRatesInterface.php | 2 +- .../OrderTaxDetailsAppliedTaxInterface.php | 2 +- .../Tax/Api/Data/OrderTaxDetailsInterface.php | 2 +- .../Api/Data/OrderTaxDetailsItemInterface.php | 2 +- .../Tax/Api/Data/QuoteDetailsInterface.php | 2 +- .../Api/Data/QuoteDetailsItemInterface.php | 2 +- .../Tax/Api/Data/TaxClassInterface.php | 2 +- .../Tax/Api/Data/TaxClassKeyInterface.php | 2 +- .../Data/TaxClassSearchResultsInterface.php | 2 +- .../Tax/Api/Data/TaxDetailsInterface.php | 2 +- .../Tax/Api/Data/TaxDetailsItemInterface.php | 2 +- .../Magento/Tax/Api/Data/TaxRateInterface.php | 2 +- .../Data/TaxRateSearchResultsInterface.php | 2 +- .../Tax/Api/Data/TaxRateTitleInterface.php | 2 +- .../Magento/Tax/Api/Data/TaxRuleInterface.php | 2 +- .../Data/TaxRuleSearchResultsInterface.php | 2 +- .../Tax/Api/OrderTaxManagementInterface.php | 2 +- .../Tax/Api/TaxCalculationInterface.php | 2 +- .../Tax/Api/TaxClassManagementInterface.php | 2 +- .../Tax/Api/TaxClassRepositoryInterface.php | 2 +- .../Tax/Api/TaxRateManagementInterface.php | 2 +- .../Tax/Api/TaxRateRepositoryInterface.php | 2 +- .../Tax/Api/TaxRuleRepositoryInterface.php | 2 +- .../Adminhtml/Frontend/Region/Updater.php | 2 +- .../Block/Adminhtml/Items/Price/Renderer.php | 2 +- .../Magento/Tax/Block/Adminhtml/Rate/Form.php | 2 +- .../Adminhtml/Rate/Grid/Renderer/Data.php | 2 +- .../Tax/Block/Adminhtml/Rate/Title.php | 2 +- .../Block/Adminhtml/Rate/Title/Fieldset.php | 2 +- .../Tax/Block/Adminhtml/Rate/Toolbar/Add.php | 2 +- .../Tax/Block/Adminhtml/Rate/Toolbar/Save.php | 2 +- app/code/Magento/Tax/Block/Adminhtml/Rule.php | 2 +- .../Magento/Tax/Block/Adminhtml/Rule/Edit.php | 2 +- .../Tax/Block/Adminhtml/Rule/Edit/Form.php | 2 +- .../Magento/Tax/Block/Checkout/Discount.php | 2 +- .../Magento/Tax/Block/Checkout/Grandtotal.php | 2 +- .../Magento/Tax/Block/Checkout/Shipping.php | 2 +- .../Tax/Block/Checkout/Shipping/Price.php | 2 +- .../Magento/Tax/Block/Checkout/Subtotal.php | 2 +- app/code/Magento/Tax/Block/Checkout/Tax.php | 2 +- .../Magento/Tax/Block/Item/Price/Renderer.php | 2 +- .../Magento/Tax/Block/Sales/Order/Tax.php | 2 +- .../Magento/Tax/Controller/Adminhtml/Rate.php | 2 +- .../Tax/Controller/Adminhtml/Rate/Add.php | 2 +- .../Controller/Adminhtml/Rate/AjaxDelete.php | 2 +- .../Controller/Adminhtml/Rate/AjaxLoad.php | 2 +- .../Controller/Adminhtml/Rate/AjaxSave.php | 2 +- .../Tax/Controller/Adminhtml/Rate/Delete.php | 2 +- .../Tax/Controller/Adminhtml/Rate/Edit.php | 2 +- .../Tax/Controller/Adminhtml/Rate/Index.php | 2 +- .../Tax/Controller/Adminhtml/Rate/Save.php | 2 +- .../Magento/Tax/Controller/Adminhtml/Rule.php | 2 +- .../Tax/Controller/Adminhtml/Rule/Delete.php | 2 +- .../Tax/Controller/Adminhtml/Rule/Edit.php | 2 +- .../Tax/Controller/Adminhtml/Rule/Index.php | 2 +- .../Controller/Adminhtml/Rule/NewAction.php | 2 +- .../Tax/Controller/Adminhtml/Rule/Save.php | 2 +- .../Magento/Tax/Controller/Adminhtml/Tax.php | 2 +- .../Controller/Adminhtml/Tax/AjaxDelete.php | 2 +- .../Tax/Controller/Adminhtml/Tax/AjaxSave.php | 2 +- .../Adminhtml/Tax/IgnoreTaxNotification.php | 2 +- .../Tax/Controller/RegistryConstants.php | 2 +- .../CheckoutTotalsJsLayoutDataProvider.php | 2 +- app/code/Magento/Tax/Helper/Data.php | 48 +- .../Tax/Model/AggregateSalesReportTaxData.php | 2 +- .../JoinProcessor/CalculationData.php | 2 +- .../JoinProcessor/CustomerTaxClass.php | 2 +- .../JoinProcessor/ProductTaxClass.php | 2 +- .../Api/SearchCriteria/JoinProcessor/Rate.php | 2 +- .../Tax/Model/App/Action/ContextPlugin.php | 2 +- app/code/Magento/Tax/Model/Calculation.php | 2 +- .../AbstractAggregateCalculator.php | 2 +- .../Model/Calculation/AbstractCalculator.php | 2 +- .../Model/Calculation/CalculatorFactory.php | 2 +- .../Model/Calculation/GrandTotalDetails.php | 2 +- .../Tax/Model/Calculation/GrandTotalRates.php | 2 +- .../Magento/Tax/Model/Calculation/Rate.php | 2 +- .../Tax/Model/Calculation/Rate/Converter.php | 2 +- .../Tax/Model/Calculation/Rate/Title.php | 2 +- .../Tax/Model/Calculation/RateFactory.php | 2 +- .../Tax/Model/Calculation/RateRegistry.php | 2 +- .../Tax/Model/Calculation/RateRepository.php | 2 +- .../Model/Calculation/RowBaseCalculator.php | 2 +- .../Magento/Tax/Model/Calculation/Rule.php | 2 +- .../Tax/Model/Calculation/Rule/Validator.php | 2 +- .../Tax/Model/Calculation/TaxRuleRegistry.php | 2 +- .../Model/Calculation/TotalBaseCalculator.php | 2 +- .../Model/Calculation/UnitBaseCalculator.php | 2 +- app/code/Magento/Tax/Model/ClassModel.php | 2 +- .../Magento/Tax/Model/ClassModelRegistry.php | 2 +- app/code/Magento/Tax/Model/Config.php | 31 +- .../Magento/Tax/Model/Config/Notification.php | 8 +- .../Tax/Model/Config/Price/IncludePrice.php | 2 +- .../Tax/Model/Config/Source/Apply/On.php | 2 +- .../Tax/Model/Config/Source/Basedon.php | 2 +- .../Tax/Model/Config/Source/Catalog.php | 2 +- .../Magento/Tax/Model/Config/TaxClass.php | 2 +- .../Tax/Model/Layout/DepersonalizePlugin.php | 2 +- .../Magento/Tax/Model/Plugin/OrderSave.php | 2 +- .../Model/Quote/GrandTotalDetailsPlugin.php | 29 +- .../Tax/Model/Quote/ToOrderConverter.php | 2 +- app/code/Magento/Tax/Model/Rate/Source.php | 2 +- .../Tax/Model/ResourceModel/Calculation.php | 2 +- .../ResourceModel/Calculation/Collection.php | 2 +- .../Model/ResourceModel/Calculation/Rate.php | 2 +- .../Calculation/Rate/Collection.php | 2 +- .../ResourceModel/Calculation/Rate/Title.php | 2 +- .../Calculation/Rate/Title/Collection.php | 2 +- .../Model/ResourceModel/Calculation/Rule.php | 2 +- .../Calculation/Rule/Collection.php | 2 +- .../Model/ResourceModel/Report/Collection.php | 2 +- .../Tax/Model/ResourceModel/Report/Tax.php | 2 +- .../ResourceModel/Report/Tax/Createdat.php | 2 +- .../ResourceModel/Report/Tax/Updatedat.php | 2 +- .../Report/Updatedat/Collection.php | 2 +- .../Model/ResourceModel/Sales/Order/Tax.php | 2 +- .../Sales/Order/Tax/Collection.php | 2 +- .../Tax/Model/ResourceModel/TaxClass.php | 2 +- .../ResourceModel/TaxClass/Collection.php | 2 +- .../Magento/Tax/Model/Sales/Order/Details.php | 2 +- .../Magento/Tax/Model/Sales/Order/Tax.php | 2 +- .../Tax/Model/Sales/Order/TaxManagement.php | 2 +- .../Tax/Model/Sales/Pdf/Grandtotal.php | 2 +- .../Magento/Tax/Model/Sales/Pdf/Shipping.php | 2 +- .../Magento/Tax/Model/Sales/Pdf/Subtotal.php | 2 +- app/code/Magento/Tax/Model/Sales/Pdf/Tax.php | 2 +- .../Tax/Model/Sales/Quote/ItemDetails.php | 2 +- .../Tax/Model/Sales/Quote/QuoteDetails.php | 2 +- .../Sales/Total/Quote/CommonTaxCollector.php | 3 +- .../Tax/Model/Sales/Total/Quote/Shipping.php | 2 +- .../Tax/Model/Sales/Total/Quote/Subtotal.php | 2 +- .../Tax/Model/Sales/Total/Quote/Tax.php | 16 +- .../Model/System/Config/Source/Algorithm.php | 2 +- .../Tax/Model/System/Config/Source/Apply.php | 2 +- .../Model/System/Config/Source/PriceType.php | 2 +- .../System/Config/Source/Tax/Country.php | 2 +- .../System/Config/Source/Tax/Display/Type.php | 2 +- .../Model/System/Config/Source/Tax/Region.php | 2 +- .../Notification/ApplyDiscountOnPrices.php | 142 + .../Message/Notification/DiscountErrors.php | 143 + .../Message/Notification/RoundingErrors.php | 151 + .../System/Message/NotificationInterface.php | 13 + .../Model/System/Message/Notifications.php | 221 +- app/code/Magento/Tax/Model/TaxCalculation.php | 2 +- .../Tax/Model/TaxClass/AbstractType.php | 2 +- .../Magento/Tax/Model/TaxClass/Factory.php | 2 +- app/code/Magento/Tax/Model/TaxClass/Key.php | 2 +- .../Magento/Tax/Model/TaxClass/Management.php | 2 +- .../Magento/Tax/Model/TaxClass/Repository.php | 2 +- .../Tax/Model/TaxClass/Source/Customer.php | 2 +- .../Tax/Model/TaxClass/Source/Product.php | 2 +- .../Tax/Model/TaxClass/Type/Customer.php | 2 +- .../Tax/Model/TaxClass/Type/Product.php | 2 +- .../Tax/Model/TaxClass/Type/TypeInterface.php | 2 +- .../Magento/Tax/Model/TaxConfigProvider.php | 2 +- .../Tax/Model/TaxDetails/AppliedTax.php | 2 +- .../Tax/Model/TaxDetails/AppliedTaxRate.php | 2 +- .../Tax/Model/TaxDetails/ItemDetails.php | 2 +- .../Tax/Model/TaxDetails/TaxDetails.php | 2 +- .../Magento/Tax/Model/TaxRateCollection.php | 2 +- .../Magento/Tax/Model/TaxRateManagement.php | 2 +- .../Magento/Tax/Model/TaxRuleCollection.php | 2 +- .../Magento/Tax/Model/TaxRuleRepository.php | 2 +- .../Tax/Observer/AfterAddressSaveObserver.php | 2 +- .../Tax/Observer/CustomerLoggedInObserver.php | 2 +- .../GetPriceConfigurationObserver.php | 2 +- .../Observer/UpdateProductOptionsObserver.php | 2 +- .../Tax/Plugin/Checkout/CustomerData/Cart.php | 2 +- app/code/Magento/Tax/Pricing/Adjustment.php | 2 +- .../Magento/Tax/Pricing/Render/Adjustment.php | 2 +- app/code/Magento/Tax/Setup/InstallData.php | 2 +- app/code/Magento/Tax/Setup/InstallSchema.php | 2 +- app/code/Magento/Tax/Setup/TaxSetup.php | 2 +- app/code/Magento/Tax/Setup/UpgradeData.php | 2 +- .../Unit/App/Action/ContextPluginTest.php | 2 +- .../Adminhtml/Items/Price/RendererTest.php | 2 +- .../Block/Checkout/Shipping/PriceTest.php | 2 +- .../Test/Unit/Block/Checkout/ShippingTest.php | 2 +- .../Unit/Block/Item/Price/RendererTest.php | 2 +- .../Adminhtml/Rate/AjaxLoadTest.php | 2 +- .../Tax/IgnoreTaxNotificationTest.php | 2 +- .../Tax/Test/Unit/GetterSetterTest.php | 2 +- .../Magento/Tax/Test/Unit/Helper/DataTest.php | 33 +- .../Calculation/CalculatorFactoryTest.php | 2 +- .../Model/Calculation/Rate/ConverterTest.php | 2 +- .../Model/Calculation/RateRegistryTest.php | 2 +- .../Model/Calculation/RateRepositoryTest.php | 2 +- .../Test/Unit/Model/Calculation/RateTest.php | 2 +- .../RowBaseAndTotalBaseCalculatorTestCase.php | 2 +- .../Calculation/RowBaseCalculatorTest.php | 2 +- .../Model/Calculation/TaxRuleRegistryTest.php | 2 +- .../Calculation/TotalBaseCalculatorTest.php | 2 +- .../Calculation/UnitBaseCalculatorTest.php | 2 +- .../Unit/Model/ClassModelRegistryTest.php | 2 +- .../Test/Unit/Model/Config/TaxClassTest.php | 2 +- .../Tax/Test/Unit/Model/ConfigTest.php | 8 +- .../Test/Unit/Model/Plugin/OrderSaveTest.php | 6 +- .../Quote/GrandTotalDetailsPluginTest.php | 27 +- .../Unit/Model/Quote/ToOrderConverterTest.php | 2 +- .../Model/ResourceModel/CalculationTest.php | 2 +- .../Model/Sales/Order/TaxManagementTest.php | 2 +- .../Total/Quote/CommonTaxCollectorTest.php | 2 +- .../Model/Sales/Total/Quote/ShippingTest.php | 2 +- .../Model/Sales/Total/Quote/SubtotalTest.php | 2 +- .../Unit/Model/Sales/Total/Quote/TaxTest.php | 62 +- .../ApplyDiscountOnPricesTest.php | 160 + .../Notification/DiscountErrorsTest.php | 103 + .../Notification/RoundingErrorsTest.php | 142 + .../System/Message/NotificationsTest.php | 97 + .../Test/Unit/Model/TaxCalculationTest.php | 2 +- .../Test/Unit/Model/TaxClass/FactoryTest.php | 2 +- .../Unit/Model/TaxClass/ManagementTest.php | 2 +- .../Unit/Model/TaxClass/RepositoryTest.php | 2 +- .../Model/TaxClass/Source/CustomerTest.php | 2 +- .../Model/TaxClass/Source/ProductTest.php | 2 +- .../Unit/Model/TaxClass/Type/CustomerTest.php | 2 +- .../Unit/Model/TaxClass/Type/ProductTest.php | 2 +- .../Test/Unit/Model/TaxConfigProviderTest.php | 2 +- .../Test/Unit/Model/TaxRateCollectionTest.php | 2 +- .../Test/Unit/Model/TaxRateManagementTest.php | 2 +- .../Test/Unit/Model/TaxRuleCollectionTest.php | 2 +- .../Test/Unit/Model/TaxRuleRepositoryTest.php | 2 +- .../Observer/AfterAddressSaveObserverTest.php | 2 +- .../Observer/CustomerLoggedInObserverTest.php | 2 +- .../GetPriceConfigurationObserverTest.php | 2 +- .../UpdateProductOptionsObserverTest.php | 2 +- .../Plugin/Checkout/CustomerData/CartTest.php | 2 +- .../Tax/Test/Unit/Pricing/AdjustmentTest.php | 2 +- .../Unit/Pricing/Render/AdjustmentTest.php | 2 +- .../Tax/Test/Unit/Setup/TaxSetupTest.php | 2 +- app/code/Magento/Tax/composer.json | 2 +- app/code/Magento/Tax/etc/acl.xml | 2 +- app/code/Magento/Tax/etc/adminhtml/di.xml | 11 +- app/code/Magento/Tax/etc/adminhtml/menu.xml | 2 +- app/code/Magento/Tax/etc/adminhtml/routes.xml | 4 +- app/code/Magento/Tax/etc/adminhtml/system.xml | 4 +- .../Magento/Tax/etc/catalog_attributes.xml | 2 +- app/code/Magento/Tax/etc/config.xml | 2 +- app/code/Magento/Tax/etc/crontab.xml | 2 +- app/code/Magento/Tax/etc/di.xml | 2 +- app/code/Magento/Tax/etc/events.xml | 2 +- .../Magento/Tax/etc/extension_attributes.xml | 2 +- app/code/Magento/Tax/etc/fieldset.xml | 2 +- app/code/Magento/Tax/etc/frontend/di.xml | 2 +- app/code/Magento/Tax/etc/frontend/events.xml | 2 +- app/code/Magento/Tax/etc/module.xml | 2 +- app/code/Magento/Tax/etc/pdf.xml | 2 +- app/code/Magento/Tax/etc/sales.xml | 2 +- app/code/Magento/Tax/etc/webapi.xml | 2 +- app/code/Magento/Tax/registration.php | 2 +- .../layout/sales_creditmemo_item_price.xml | 2 +- .../layout/sales_invoice_item_price.xml | 2 +- .../layout/sales_order_create_item_price.xml | 2 +- .../layout/sales_order_item_price.xml | 2 +- .../view/adminhtml/layout/tax_rate_block.xml | 2 +- .../adminhtml/layout/tax_rate_exportcsv.xml | 2 +- .../adminhtml/layout/tax_rate_exportxml.xml | 2 +- .../view/adminhtml/layout/tax_rate_index.xml | 2 +- .../view/adminhtml/layout/tax_rule_block.xml | 2 +- .../view/adminhtml/layout/tax_rule_edit.xml | 2 +- .../view/adminhtml/layout/tax_rule_index.xml | 2 +- .../adminhtml/templates/class/page/edit.phtml | 2 +- .../adminhtml/templates/items/price/row.phtml | 2 +- .../templates/items/price/total.phtml | 2 +- .../templates/items/price/unit.phtml | 2 +- .../order/create/items/price/row.phtml | 2 +- .../order/create/items/price/total.phtml | 2 +- .../order/create/items/price/unit.phtml | 2 +- .../view/adminhtml/templates/rate/form.phtml | 2 +- .../view/adminhtml/templates/rate/js.phtml | 2 +- .../view/adminhtml/templates/rate/title.phtml | 2 +- .../view/adminhtml/templates/rule/edit.phtml | 2 +- .../adminhtml/templates/rule/rate/form.phtml | 2 +- .../templates/toolbar/class/add.phtml | 2 +- .../templates/toolbar/class/save.phtml | 2 +- .../templates/toolbar/rate/add.phtml | 2 +- .../templates/toolbar/rate/save.phtml | 2 +- .../templates/toolbar/rule/add.phtml | 2 +- .../templates/toolbar/rule/save.phtml | 2 +- .../Tax/view/adminhtml/web/js/bootstrap.js | 5 +- .../base/layout/catalog_product_prices.xml | 2 +- .../base/templates/pricing/adjustment.phtml | 2 +- .../templates/pricing/adjustment/bundle.phtml | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../checkout_cart_sidebar_total_renderers.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 4 +- .../layout/checkout_item_price_renderers.xml | 2 +- .../layout/sales_email_item_price.xml | 2 +- .../layout/sales_order_item_price.xml | 2 +- .../checkout/cart/item/price/sidebar.phtml | 2 +- .../templates/checkout/discount.phtml | 2 +- .../templates/checkout/grandtotal.phtml | 2 +- .../templates/checkout/shipping.phtml | 4 +- .../templates/checkout/shipping/price.phtml | 2 +- .../templates/checkout/subtotal.phtml | 2 +- .../frontend/templates/checkout/tax.phtml | 2 +- .../templates/email/items/price/row.phtml | 2 +- .../frontend/templates/item/price/row.phtml | 2 +- .../item/price/total_after_discount.phtml | 2 +- .../frontend/templates/item/price/unit.phtml | 2 +- .../view/frontend/templates/order/tax.phtml | 2 +- .../view/checkout/cart/totals/grand-total.js | 31 +- .../js/view/checkout/cart/totals/shipping.js | 45 +- .../web/js/view/checkout/cart/totals/tax.js | 67 +- .../view/checkout/minicart/subtotal/totals.js | 3 +- .../js/view/checkout/shipping_method/price.js | 60 +- .../js/view/checkout/summary/grand-total.js | 129 +- .../checkout/summary/item/details/subtotal.js | 80 +- .../web/js/view/checkout/summary/shipping.js | 117 +- .../web/js/view/checkout/summary/subtotal.js | 95 +- .../web/js/view/checkout/summary/tax.js | 170 +- .../checkout/cart/totals/grand-total.html | 2 +- .../checkout/cart/totals/shipping.html | 2 +- .../template/checkout/cart/totals/tax.html | 2 +- .../checkout/minicart/subtotal/totals.html | 2 +- .../checkout/shipping_method/price.html | 2 +- .../checkout/summary/grand-total.html | 2 +- .../summary/item/details/subtotal.html | 4 +- .../template/checkout/summary/shipping.html | 2 +- .../template/checkout/summary/subtotal.html | 2 +- .../web/template/checkout/summary/tax.html | 2 +- .../Adminhtml/Rate/Grid/Renderer/Country.php | 2 +- .../Block/Adminhtml/Rate/ImportExport.php | 2 +- .../Adminhtml/Rate/ImportExportHeader.php | 2 +- .../Controller/Adminhtml/Rate.php | 2 +- .../Controller/Adminhtml/Rate/ExportCsv.php | 2 +- .../Controller/Adminhtml/Rate/ExportPost.php | 4 +- .../Controller/Adminhtml/Rate/ExportXml.php | 2 +- .../Adminhtml/Rate/ImportExport.php | 2 +- .../Controller/Adminhtml/Rate/ImportPost.php | 2 +- .../Model/Rate/CsvImportHandler.php | 2 +- .../Adminhtml/Rate/ExportPostTest.php | 103 + .../Magento/TaxImportExport/composer.json | 2 +- app/code/Magento/TaxImportExport/etc/acl.xml | 2 +- .../TaxImportExport/etc/adminhtml/menu.xml | 2 +- .../TaxImportExport/etc/adminhtml/routes.xml | 2 +- .../Magento/TaxImportExport/etc/module.xml | 2 +- .../Magento/TaxImportExport/registration.php | 2 +- .../view/adminhtml/layout/tax_rate_block.xml | 2 +- .../view/adminhtml/layout/tax_rule_edit.xml | 2 +- .../adminhtml/templates/importExport.phtml | 2 +- .../templates/importExportHeader.phtml | 2 +- .../Api/Data/DesignConfigDataInterface.php | 2 +- .../Theme/Api/Data/DesignConfigInterface.php | 2 +- .../Api/DesignConfigRepositoryInterface.php | 2 +- .../Design/Config/Edit/BackButton.php | 2 +- .../Config/Edit/SaveAndContinueButton.php | 2 +- .../Design/Config/Edit/SaveButton.php | 2 +- .../Adminhtml/Design/Config/Edit/Scope.php | 2 +- .../Block/Adminhtml/System/Design/Theme.php | 2 +- .../Adminhtml/System/Design/Theme/Edit.php | 2 +- .../System/Design/Theme/Edit/AbstractTab.php | 2 +- .../System/Design/Theme/Edit/Form.php | 2 +- .../Design/Theme/Edit/Form/Element/File.php | 2 +- .../Design/Theme/Edit/Form/Element/Image.php | 2 +- .../Design/Theme/Edit/Form/Element/Links.php | 2 +- .../System/Design/Theme/Edit/Tab/Css.php | 2 +- .../System/Design/Theme/Edit/Tab/General.php | 2 +- .../System/Design/Theme/Edit/Tab/Js.php | 2 +- .../System/Design/Theme/Edit/Tabs.php | 2 +- .../Block/Adminhtml/Wysiwyg/Files/Content.php | 2 +- .../Adminhtml/Wysiwyg/Files/Content/Files.php | 2 +- .../Wysiwyg/Files/Content/Uploader.php | 2 +- .../Block/Adminhtml/Wysiwyg/Files/Tree.php | 2 +- .../Magento/Theme/Block/Html/Breadcrumbs.php | 2 +- app/code/Magento/Theme/Block/Html/Footer.php | 2 +- app/code/Magento/Theme/Block/Html/Header.php | 2 +- .../Magento/Theme/Block/Html/Header/Logo.php | 2 +- app/code/Magento/Theme/Block/Html/Notices.php | 2 +- app/code/Magento/Theme/Block/Html/Pager.php | 2 +- app/code/Magento/Theme/Block/Html/Title.php | 2 +- app/code/Magento/Theme/Block/Html/Topmenu.php | 45 +- app/code/Magento/Theme/Block/Html/Welcome.php | 2 +- .../Console/Command/ThemeUninstallCommand.php | 2 +- .../Adminhtml/Design/Config/Edit.php | 2 +- .../Design/Config/FileUploader/Save.php | 2 +- .../Adminhtml/Design/Config/Index.php | 2 +- .../Adminhtml/Design/Config/Save.php | 2 +- .../Adminhtml/System/Design/Theme.php | 2 +- .../Adminhtml/System/Design/Theme/Delete.php | 2 +- .../System/Design/Theme/DownloadCss.php | 2 +- .../System/Design/Theme/DownloadCustomCss.php | 2 +- .../Adminhtml/System/Design/Theme/Edit.php | 2 +- .../Adminhtml/System/Design/Theme/Grid.php | 2 +- .../Adminhtml/System/Design/Theme/Index.php | 2 +- .../System/Design/Theme/NewAction.php | 2 +- .../Adminhtml/System/Design/Theme/Save.php | 2 +- .../System/Design/Theme/UploadCss.php | 2 +- .../System/Design/Theme/UploadJs.php | 2 +- .../Adminhtml/System/Design/Wysiwyg/Files.php | 2 +- .../System/Design/Wysiwyg/Files/Contents.php | 2 +- .../Design/Wysiwyg/Files/DeleteFiles.php | 2 +- .../Design/Wysiwyg/Files/DeleteFolder.php | 2 +- .../System/Design/Wysiwyg/Files/Index.php | 2 +- .../System/Design/Wysiwyg/Files/NewFolder.php | 2 +- .../System/Design/Wysiwyg/Files/OnInsert.php | 2 +- .../Design/Wysiwyg/Files/PreviewImage.php | 2 +- .../System/Design/Wysiwyg/Files/TreeJson.php | 2 +- .../System/Design/Wysiwyg/Files/Upload.php | 2 +- .../Theme/Controller/Result/MessagePlugin.php | 43 +- .../Magento/Theme/CustomerData/Messages.php | 2 +- app/code/Magento/Theme/Helper/Storage.php | 2 +- app/code/Magento/Theme/Helper/Theme.php | 2 +- app/code/Magento/Theme/Model/Config.php | 2 +- .../Theme/Model/Config/Customization.php | 2 +- app/code/Magento/Theme/Model/CopyService.php | 2 +- .../Theme/Model/Data/Design/Config.php | 2 +- .../Theme/Model/Data/Design/Config/Data.php | 2 +- .../Theme/Model/Data/Design/ConfigFactory.php | 2 +- app/code/Magento/Theme/Model/Design.php | 18 +- .../Theme/Model/Design/Backend/Exceptions.php | 18 +- .../Theme/Model/Design/Backend/Favicon.php | 2 +- .../Theme/Model/Design/Backend/File.php | 42 +- .../Theme/Model/Design/Backend/Image.php | 2 +- .../Theme/Model/Design/Backend/Logo.php | 2 +- .../Theme/Model/Design/Backend/Theme.php | 2 +- .../Model/Design/BackendModelFactory.php | 2 +- .../Model/Design/Config/DataProvider.php | 101 +- .../Design/Config/DataProvider/DataLoader.php | 2 +- .../Config/DataProvider/MetadataLoader.php | 2 +- .../Config/FileUploader/FileProcessor.php | 7 +- .../Model/Design/Config/MetadataProvider.php | 2 +- .../Config/MetadataProviderInterface.php | 2 +- .../Theme/Model/Design/Config/Plugin.php | 2 +- .../Theme/Model/Design/Config/Storage.php | 2 +- .../Theme/Model/Design/Config/Validator.php | 2 +- .../Model/Design/Config/ValueChecker.php | 2 +- .../Model/Design/Config/ValueProcessor.php | 2 +- .../Theme/Model/Design/Theme/Label.php | 2 +- .../Theme/Model/DesignConfigRepository.php | 2 +- .../Magento/Theme/Model/Favicon/Favicon.php | 2 +- .../Theme/Model/Indexer/Design/Config.php | 2 +- .../Indexer/Design/Config/FieldsProvider.php | 2 +- .../Indexer/Design/Config/Plugin/Store.php | 2 +- .../Design/Config/Plugin/StoreGroup.php | 2 +- .../Indexer/Design/Config/Plugin/Website.php | 2 +- .../Magento/Theme/Model/Layout/Config.php | 2 +- .../Theme/Model/Layout/Config/Converter.php | 2 +- .../Theme/Model/Layout/Config/Reader.php | 2 +- .../Model/Layout/Config/SchemaLocator.php | 2 +- .../Theme/Model/Layout/Source/Layout.php | 2 +- .../Theme/Model/PageLayout/Config/Builder.php | 2 +- .../Theme/Model/ResourceModel/Design.php | 2 +- .../Model/ResourceModel/Design/Collection.php | 2 +- .../Model/ResourceModel/Design/Config.php | 2 +- .../Design/Config/Collection.php | 2 +- .../Design/Config/Scope/Collection.php | 2 +- .../Theme/Model/ResourceModel/Theme.php | 2 +- .../Model/ResourceModel/Theme/Collection.php | 2 +- .../Theme/Customization/Update.php | 2 +- .../ResourceModel/Theme/Data/Collection.php | 2 +- .../Theme/Model/ResourceModel/Theme/File.php | 2 +- .../ResourceModel/Theme/File/Collection.php | 2 +- .../ResourceModel/Theme/Grid/Collection.php | 2 +- .../Theme/Model/Source/InitialThemeSource.php | 126 + app/code/Magento/Theme/Model/Theme.php | 84 +- .../Magento/Theme/Model/Theme/Collection.php | 2 +- .../Model/Theme/Customization/Config.php | 2 +- .../Theme/Customization/File/CustomCss.php | 2 +- app/code/Magento/Theme/Model/Theme/Data.php | 2 +- .../Theme/Model/Theme/Data/Collection.php | 2 +- .../Theme/Model/Theme/Domain/Physical.php | 2 +- .../Theme/Model/Theme/Domain/Staging.php | 2 +- .../Theme/Model/Theme/Domain/Virtual.php | 2 +- app/code/Magento/Theme/Model/Theme/File.php | 2 +- .../Theme/Model/Theme/FileProvider.php | 2 +- .../Magento/Theme/Model/Theme/Image/Path.php | 2 +- .../Theme/Model/Theme/Plugin/Registration.php | 2 +- .../Theme/Model/Theme/Registration.php | 2 +- .../Magento/Theme/Model/Theme/Resolver.php | 2 +- .../Magento/Theme/Model/Theme/SingleFile.php | 2 +- .../Theme/Model/Theme/Source/Theme.php | 2 +- .../Model/Theme/ThemeDependencyChecker.php | 2 +- .../Theme/Model/Theme/ThemePackageInfo.php | 2 +- .../Theme/Model/Theme/ThemeProvider.php | 136 +- .../Theme/Model/Theme/ThemeUninstaller.php | 2 +- .../Magento/Theme/Model/ThemeValidator.php | 2 +- .../Magento/Theme/Model/Uploader/Service.php | 2 +- .../Theme/Model/Url/Plugin/Signature.php | 2 +- app/code/Magento/Theme/Model/View/Design.php | 2 +- .../Magento/Theme/Model/Wysiwyg/Storage.php | 2 +- .../ApplyThemeCustomizationObserver.php | 2 +- .../Observer/CheckThemeIsAssignedObserver.php | 2 +- .../CleanThemeRelatedContentObserver.php | 2 +- app/code/Magento/Theme/Setup/InstallData.php | 2 +- .../Magento/Theme/Setup/InstallSchema.php | 2 +- .../Magento/Theme/Setup/RecurringData.php | 2 +- app/code/Magento/Theme/Setup/UpgradeData.php | 57 +- .../Design/Config/Edit/BackButtonTest.php | 2 +- .../Design/Config/Edit/SaveButtonTest.php | 2 +- .../Design/Config/Edit/ScopeTest.php | 2 +- .../Theme/Edit/Form/Element/FileTest.php | 2 +- .../System/Design/Theme/Edit/FormTest.php | 2 +- .../System/Design/Theme/Tab/CssTest.php | 2 +- .../System/Design/Theme/Tab/JsTest.php | 2 +- .../System/Design/Theme/TabAbstractTest.php | 2 +- .../Adminhtml/Wysiwyg/Files/ContentTest.php | 2 +- .../Adminhtml/Wysiwyg/Files/TreeTest.php | 2 +- .../Theme/Test/Unit/Block/Html/FooterTest.php | 2 +- .../Test/Unit/Block/Html/Header/LogoTest.php | 2 +- .../Theme/Test/Unit/Block/Html/HeaderTest.php | 2 +- .../Theme/Test/Unit/Block/Html/TitleTest.php | 2 +- .../Test/Unit/Block/Html/TopmenuTest.php | 187 +- .../Command/ThemeUninstallCommandTest.php | 2 +- .../Adminhtml/Design/Config/EditTest.php | 2 +- .../Design/Config/FileUploader/SaveTest.php | 2 +- .../Adminhtml/Design/Config/IndexTest.php | 2 +- .../Adminhtml/Design/Config/SaveTest.php | 2 +- .../System/Design/Theme/DeleteTest.php | 2 +- .../System/Design/Theme/DownloadCssTest.php | 2 +- .../Design/Theme/DownloadCustomCssTest.php | 2 +- .../System/Design/Theme/EditTest.php | 2 +- .../System/Design/Theme/GridTest.php | 2 +- .../System/Design/Theme/IndexTest.php | 6 +- .../System/Design/Theme/SaveTest.php | 2 +- .../System/Design/Theme/UploadCssTest.php | 2 +- .../System/Design/Theme/UploadJsTest.php | 2 +- .../Adminhtml/System/Design/ThemeTest.php | 2 +- .../Design/Wysiwyg/Files/ContentsTest.php | 2 +- .../Design/Wysiwyg/Files/DeleteFilesTest.php | 2 +- .../Design/Wysiwyg/Files/DeleteFolderTest.php | 2 +- .../System/Design/Wysiwyg/Files/IndexTest.php | 2 +- .../Design/Wysiwyg/Files/OnInsertTest.php | 2 +- .../Controller/Result/MessagePluginTest.php | 57 +- .../Test/Unit/CustomerData/MessagesTest.php | 2 +- .../Theme/Test/Unit/Helper/StorageTest.php | 2 +- .../Theme/Test/Unit/Helper/ThemeTest.php | 2 +- .../Unit/Model/Config/CustomizationTest.php | 2 +- .../Test/Unit/Model/Config/ValidatorTest.php | 2 +- .../Theme/Test/Unit/Model/ConfigTest.php | 2 +- .../Theme/Test/Unit/Model/CopyServiceTest.php | 2 +- .../Model/Data/Design/ConfigFactoryTest.php | 2 +- .../Model/Design/Backend/ExceptionsTest.php | 7 +- .../Unit/Model/Design/Backend/FileTest.php | 34 +- .../Unit/Model/Design/Backend/ThemeTest.php | 2 +- .../Model/Design/BackendModelFactoryTest.php | 2 +- .../Config/DataProvider/DataLoaderTest.php | 2 +- .../DataProvider/MetadataLoaderTest.php | 2 +- .../Model/Design/Config/DataProviderTest.php | 170 +- .../Config/FileUploader/FileProcessorTest.php | 11 +- .../Unit/Model/Design/Config/PluginTest.php | 2 +- .../Unit/Model/Design/Config/StorageTest.php | 2 +- .../Model/Design/Config/ValueCheckerTest.php | 2 +- .../Design/Config/ValueProcessorTest.php | 2 +- .../Unit/Model/DesignConfigRepositoryTest.php | 2 +- .../Theme/Test/Unit/Model/DesignTest.php | 58 +- .../Test/Unit/Model/Favicon/FaviconTest.php | 2 +- .../Design/Config/Plugin/StoreGroupTest.php | 2 +- .../Design/Config/Plugin/StoreTest.php | 2 +- .../Design/Config/Plugin/WebsiteTest.php | 2 +- .../Unit/Model/Indexer/Design/ConfigTest.php | 2 +- .../Model/Layout/Config/ConverterTest.php | 2 +- .../Model/Layout/Config/SchemaLocatorTest.php | 2 +- .../Layout/Config/_files/page_layouts.xml | 2 +- .../Test/Unit/Model/Layout/ConfigTest.php | 2 +- .../Unit/Model/Layout/Source/LayoutTest.php | 2 +- .../Model/PageLayout/Config/BuilderTest.php | 2 +- .../Design/Config/Scope/CollectionTest.php | 2 +- .../Model/Source/InitialThemeSourceTest.php | 200 + .../Test/Unit/Model/Theme/CollectionTest.php | 2 +- .../Model/Theme/Customization/ConfigTest.php | 2 +- .../Customization/File/CustomCssTest.php | 2 +- .../Theme/Test/Unit/Model/Theme/DataTest.php | 2 +- .../Unit/Model/Theme/Domain/PhysicalTest.php | 2 +- .../Unit/Model/Theme/Domain/StagingTest.php | 2 +- .../Unit/Model/Theme/Domain/VirtualTest.php | 2 +- .../Unit/Model/Theme/FileProviderTest.php | 2 +- .../Theme/Test/Unit/Model/Theme/FileTest.php | 2 +- .../Test/Unit/Model/Theme/Image/PathTest.php | 2 +- .../Model/Theme/Plugin/RegistrationTest.php | 2 +- .../Unit/Model/Theme/RegistrationTest.php | 2 +- .../Test/Unit/Model/Theme/ResolverTest.php | 2 +- .../Test/Unit/Model/Theme/SingleFileTest.php | 2 +- .../Unit/Model/Theme/Source/ThemeTest.php | 2 +- .../Theme/ThemeDependencyCheckerTest.php | 2 +- .../Unit/Model/Theme/ThemePackageInfoTest.php | 2 +- .../Unit/Model/Theme/ThemeProviderTest.php | 279 +- .../Unit/Model/Theme/ThemeUninstallerTest.php | 2 +- .../Test/Unit/Model/Theme/ValidationTest.php | 2 +- .../Theme/Test/Unit/Model/ThemeTest.php | 161 +- .../Test/Unit/Model/ThemeValidatorTest.php | 2 +- .../Test/Unit/Model/Uploader/ServiceTest.php | 2 +- .../Unit/Model/Url/Plugin/SignatureTest.php | 2 +- .../Theme/Test/Unit/Model/View/DesignTest.php | 2 +- .../Test/Unit/Model/Wysiwyg/StorageTest.php | 2 +- .../_files/frontend/magento_iphone/theme.xml | 2 +- .../frontend/magento_iphone/theme_invalid.xml | 2 +- .../ApplyThemeCustomizationObserverTest.php | 2 +- .../CheckThemeIsAssignedObserverTest.php | 2 +- .../CleanThemeRelatedContentObserverTest.php | 2 +- .../Config/SearchRobots/ResetButtonTest.php | 2 +- .../Listing/Column/EditActionTest.php | 2 +- .../Component/Design/Config/DataProvider.php | 2 +- .../Config/SearchRobots/ResetButton.php | 4 +- .../Component/Listing/Column/EditAction.php | 2 +- app/code/Magento/Theme/composer.json | 5 +- app/code/Magento/Theme/etc/acl.xml | 2 +- app/code/Magento/Theme/etc/adminhtml/di.xml | 2 +- app/code/Magento/Theme/etc/adminhtml/menu.xml | 2 +- .../Magento/Theme/etc/adminhtml/routes.xml | 2 +- app/code/Magento/Theme/etc/config.xml | 4 +- app/code/Magento/Theme/etc/di.xml | 39 +- app/code/Magento/Theme/etc/events.xml | 2 +- .../Theme/etc/extension_attributes.xml | 2 +- app/code/Magento/Theme/etc/frontend/di.xml | 2 +- .../Magento/Theme/etc/frontend/events.xml | 2 +- .../Magento/Theme/etc/frontend/sections.xml | 2 +- app/code/Magento/Theme/etc/indexer.xml | 2 +- app/code/Magento/Theme/etc/module.xml | 4 +- app/code/Magento/Theme/etc/mview.xml | 2 +- app/code/Magento/Theme/i18n/en_US.csv | 2 +- app/code/Magento/Theme/registration.php | 2 +- .../adminhtml_system_design_theme_block.xml | 2 +- .../adminhtml_system_design_theme_edit.xml | 2 +- .../adminhtml_system_design_theme_grid.xml | 2 +- .../adminhtml_system_design_theme_index.xml | 2 +- ...l_system_design_wysiwyg_files_contents.xml | 2 +- ...html_system_design_wysiwyg_files_index.xml | 2 +- .../layout/theme_design_config_edit.xml | 2 +- .../layout/theme_design_config_index.xml | 2 +- .../Magento/Theme/view/adminhtml/layouts.xml | 2 +- .../adminhtml/page_layout/admin-1column.xml | 2 +- .../page_layout/admin-2columns-left.xml | 2 +- .../adminhtml/page_layout/admin-empty.xml | 2 +- .../adminhtml/page_layout/admin-login.xml | 2 +- .../Theme/view/adminhtml/requirejs-config.js | 90 +- .../adminhtml/templates/browser/content.phtml | 2 +- .../templates/browser/content/files.phtml | 2 +- .../templates/browser/content/uploader.phtml | 5 +- .../templates/design/config/edit/scope.phtml | 2 +- .../view/adminhtml/templates/tabs/css.phtml | 2 +- .../templates/tabs/fieldset/js.phtml | 2 +- .../view/adminhtml/templates/tabs/js.phtml | 2 +- .../view/adminhtml/templates/title.phtml | 2 +- .../ui_component/design_config_form.xml | 2 +- .../ui_component/design_config_listing.xml | 2 +- .../Theme/view/adminhtml/web/css/theme.css | 2 +- .../Theme/view/adminhtml/web/js/bootstrap.js | 11 +- .../view/adminhtml/web/js/custom-js-list.js | 23 +- .../Theme/view/adminhtml/web/js/form.js | 33 +- .../js/form/component/robots-reset-button.js | 2 +- .../Theme/view/adminhtml/web/js/sortable.js | 53 +- .../view/adminhtml/web/prototype/magento.css | 2 +- .../web/template/form/button-field.html | 4 +- .../web/template/form/element/button.html | 4 +- app/code/Magento/Theme/view/base/layouts.xml | 2 +- .../Theme/view/base/page_layout/empty.xml | 2 +- .../Theme/view/base/requirejs-config.js | 96 +- .../Theme/view/base/templates/root.phtml | 2 +- .../Theme/view/frontend/layout/default.xml | 4 +- .../frontend/layout/default_head_blocks.xml | 2 +- .../view/frontend/layout/page_calendar.xml | 2 +- .../Theme/view/frontend/layout/print.xml | 2 +- .../Magento/Theme/view/frontend/layouts.xml | 2 +- .../view/frontend/page_layout/1column.xml | 2 +- .../frontend/page_layout/2columns-left.xml | 2 +- .../frontend/page_layout/2columns-right.xml | 2 +- .../view/frontend/page_layout/3columns.xml | 2 +- .../Theme/view/frontend/requirejs-config.js | 62 +- .../templates/callouts/left_col.phtml | 2 +- .../templates/callouts/right_col.phtml | 2 +- .../templates/html/absolute_footer.phtml | 2 +- .../view/frontend/templates/html/block.phtml | 2 +- .../frontend/templates/html/breadcrumbs.phtml | 2 +- .../frontend/templates/html/bugreport.phtml | 2 +- .../frontend/templates/html/collapsible.phtml | 2 +- .../frontend/templates/html/container.phtml | 2 +- .../frontend/templates/html/copyright.phtml | 2 +- .../view/frontend/templates/html/footer.phtml | 2 +- .../view/frontend/templates/html/header.phtml | 2 +- .../frontend/templates/html/header/logo.phtml | 2 +- .../frontend/templates/html/messages.phtml | 4 +- .../frontend/templates/html/notices.phtml | 2 +- .../view/frontend/templates/html/pager.phtml | 2 +- .../view/frontend/templates/html/print.phtml | 2 +- .../frontend/templates/html/sections.phtml | 2 +- .../view/frontend/templates/html/skip.phtml | 2 +- .../frontend/templates/html/skiptarget.phtml | 2 +- .../view/frontend/templates/html/title.phtml | 2 +- .../frontend/templates/html/topmenu.phtml | 2 +- .../view/frontend/templates/js/calendar.phtml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../view/frontend/templates/js/cookie.phtml | 2 +- .../Theme/view/frontend/templates/link.phtml | 2 +- .../view/frontend/templates/messages.phtml | 2 +- .../templates/page/js/require_js.phtml | 2 +- .../view/frontend/templates/template.phtml | 2 +- .../Theme/view/frontend/templates/text.phtml | 2 +- .../Theme/view/frontend/web/css/tabs.css | 2 +- .../Theme/view/frontend/web/css/validate.css | 4 +- .../Theme/view/frontend/web/js/row-builder.js | 25 +- .../Theme/view/frontend/web/js/truncate.js | 25 +- .../view/frontend/web/js/view/messages.js | 8 +- .../Magento/Theme/view/frontend/web/menu.js | 234 +- .../view/frontend/web/prototype/magento.css | 2 +- .../App/Config/Type/Translation.php | 62 + .../Translation/Block/Html/Head/Config.php | 2 +- app/code/Magento/Translation/Block/Js.php | 2 +- .../Command/UninstallLanguageCommand.php | 2 +- .../Translation/Controller/Ajax/Index.php | 2 +- .../Magento/Translation/Model/FileManager.php | 2 +- .../Translation/Model/Inline/CacheManager.php | 2 +- .../Translation/Model/Inline/Config.php | 2 +- .../Translation/Model/Inline/Parser.php | 2 +- .../Magento/Translation/Model/Js/Config.php | 2 +- .../Model/Js/Config/Source/Strategy.php | 2 +- .../Translation/Model/Js/DataProvider.php | 24 +- .../Model/Js/DataProviderInterface.php | 2 +- .../Translation/Model/Js/PreProcessor.php | 2 +- .../Translation/Model/Json/PreProcessor.php | 2 +- .../Model/ResourceModel/StringUtils.php | 2 +- .../Model/ResourceModel/Translate.php | 93 +- .../Model/Source/InitialTranslationSource.php | 82 + .../Magento/Translation/Model/StringUtils.php | 2 +- .../Translation/Setup/InstallSchema.php | 2 +- .../Unit/App/Config/Type/TranslationTest.php | 55 + .../Translation/Test/Unit/Block/JsTest.php | 2 +- .../Command/UninstallLanguageCommandTest.php | 2 +- .../Test/Unit/Model/FileManagerTest.php | 2 +- .../Unit/Model/Inline/CacheManagerTest.php | 2 +- .../Test/Unit/Model/Inline/ConfigTest.php | 2 +- .../Test/Unit/Model/Inline/ParserTest.php | 2 +- .../Model/Js/Config/Source/StrategyTest.php | 2 +- .../Test/Unit/Model/Js/ConfigTest.php | 2 +- .../Test/Unit/Model/Js/DataProviderTest.php | 2 +- .../Test/Unit/Model/Js/PreProcessorTest.php | 2 +- .../Test/Unit/Model/Json/PreProcessorTest.php | 2 +- .../Source/InitialTranslationSourceTest.php | 159 + app/code/Magento/Translation/composer.json | 5 +- .../Magento/Translation/etc/adminhtml/di.xml | 2 +- .../Translation/etc/adminhtml/routes.xml | 4 +- .../Translation/etc/adminhtml/system.xml | 2 +- app/code/Magento/Translation/etc/cache.xml | 2 +- app/code/Magento/Translation/etc/config.xml | 2 +- app/code/Magento/Translation/etc/di.xml | 47 +- .../Translation/etc/frontend/routes.xml | 4 +- app/code/Magento/Translation/etc/module.xml | 2 +- app/code/Magento/Translation/registration.php | 2 +- .../templates/translate_inline.phtml | 2 +- .../view/base/templates/translate.phtml | 4 +- .../view/base/web/js/i18n-config.js | 5 +- .../view/frontend/requirejs-config.js | 6 +- .../frontend/templates/translate_inline.phtml | 2 +- .../view/frontend/web/add-class.js | 2 +- .../Ui/Api/BookmarkManagementInterface.php | 2 +- .../Ui/Api/BookmarkRepositoryInterface.php | 2 +- .../Api/Data/BookmarkExtensionInterface.php | 2 +- .../Magento/Ui/Api/Data/BookmarkInterface.php | 2 +- .../Data/BookmarkSearchResultsInterface.php | 2 +- .../Ui/Block/Component/StepsWizard.php | 2 +- .../Component/StepsWizard/StepAbstract.php | 2 +- .../Component/StepsWizard/StepInterface.php | 2 +- app/code/Magento/Ui/Block/Logger.php | 2 +- .../Ui/Component/AbstractComponent.php | 2 +- app/code/Magento/Ui/Component/Action.php | 2 +- app/code/Magento/Ui/Component/Bookmark.php | 2 +- app/code/Magento/Ui/Component/Container.php | 2 +- .../Magento/Ui/Component/Control/Action.php | 2 +- .../Ui/Component/Control/ActionPool.php | 2 +- .../Magento/Ui/Component/Control/Button.php | 2 +- .../Ui/Component/Control/Container.php | 2 +- .../Magento/Ui/Component/Control/Item.php | 2 +- .../Magento/Ui/Component/Control/Link.php | 2 +- .../Ui/Component/Control/SplitButton.php | 2 +- app/code/Magento/Ui/Component/DataSource.php | 2 +- app/code/Magento/Ui/Component/DynamicRows.php | 2 +- .../Magento/Ui/Component/ExportButton.php | 2 +- app/code/Magento/Ui/Component/Filters.php | 2 +- .../Ui/Component/Filters/FilterModifier.php | 2 +- .../Component/Filters/Type/AbstractFilter.php | 2 +- .../Ui/Component/Filters/Type/Date.php | 2 +- .../Ui/Component/Filters/Type/DateRange.php | 2 +- .../Ui/Component/Filters/Type/Input.php | 2 +- .../Ui/Component/Filters/Type/Range.php | 2 +- .../Ui/Component/Filters/Type/Search.php | 2 +- .../Ui/Component/Filters/Type/Select.php | 2 +- app/code/Magento/Ui/Component/Form.php | 2 +- .../Ui/Component/Form/AttributeMapper.php | 2 +- .../Magento/Ui/Component/Form/Collection.php | 2 +- .../Form/Element/AbstractElement.php | 2 +- .../Form/Element/AbstractOptionsField.php | 2 +- .../Component/Form/Element/ActionDelete.php | 2 +- .../Ui/Component/Form/Element/Checkbox.php | 2 +- .../Ui/Component/Form/Element/CheckboxSet.php | 2 +- .../Element/DataType/AbstractDataType.php | 2 +- .../Form/Element/DataType/Boolean.php | 2 +- .../Element/DataType/DataTypeInterface.php | 2 +- .../Component/Form/Element/DataType/Date.php | 18 +- .../Component/Form/Element/DataType/Email.php | 2 +- .../Component/Form/Element/DataType/Media.php | 2 +- .../Form/Element/DataType/Number.php | 2 +- .../Form/Element/DataType/Password.php | 2 +- .../Component/Form/Element/DataType/Price.php | 2 +- .../Component/Form/Element/DataType/Text.php | 2 +- .../Form/Element/ElementInterface.php | 2 +- .../Ui/Component/Form/Element/Hidden.php | 2 +- .../Ui/Component/Form/Element/Input.php | 2 +- .../Ui/Component/Form/Element/MultiSelect.php | 2 +- .../Ui/Component/Form/Element/Multiline.php | 2 +- .../Ui/Component/Form/Element/Radio.php | 2 +- .../Ui/Component/Form/Element/RadioSet.php | 2 +- .../Ui/Component/Form/Element/Range.php | 2 +- .../Ui/Component/Form/Element/Select.php | 2 +- .../Ui/Component/Form/Element/Textarea.php | 2 +- .../Ui/Component/Form/Element/Wysiwyg.php | 2 +- app/code/Magento/Ui/Component/Form/Field.php | 2 +- .../Magento/Ui/Component/Form/Fieldset.php | 2 +- .../Ui/Component/Form/Fieldset/Factory.php | 2 +- app/code/Magento/Ui/Component/HtmlContent.php | 2 +- app/code/Magento/Ui/Component/Layout.php | 2 +- app/code/Magento/Ui/Component/Layout/Tabs.php | 2 +- .../Magento/Ui/Component/Layout/Tabs/Nav.php | 2 +- .../Magento/Ui/Component/Layout/Tabs/Tab.php | 2 +- .../Ui/Component/Layout/Tabs/TabInterface.php | 2 +- .../Ui/Component/Layout/Tabs/TabWrapper.php | 2 +- app/code/Magento/Ui/Component/Listing.php | 2 +- .../Magento/Ui/Component/Listing/Columns.php | 2 +- .../Ui/Component/Listing/Columns/Column.php | 2 +- .../Listing/Columns/ColumnInterface.php | 2 +- .../Ui/Component/Listing/Columns/Date.php | 2 +- .../Ui/Component/Listing/RowInterface.php | 2 +- app/code/Magento/Ui/Component/MassAction.php | 2 +- .../Component/MassAction/Columns/Column.php | 2 +- .../Ui/Component/MassAction/Filter.php | 75 +- app/code/Magento/Ui/Component/Modal.php | 2 +- app/code/Magento/Ui/Component/Paging.php | 2 +- .../Magento/Ui/Component/Wrapper/Block.php | 2 +- .../Ui/Component/Wrapper/UiComponent.php | 2 +- .../Magento/Ui/Component/Wysiwyg/Config.php | 2 +- .../Ui/Component/Wysiwyg/ConfigInterface.php | 2 +- .../Controller/Adminhtml/AbstractAction.php | 2 +- .../Controller/Adminhtml/Bookmark/Delete.php | 2 +- .../Ui/Controller/Adminhtml/Bookmark/Save.php | 2 +- .../Controller/Adminhtml/Export/GridToCsv.php | 2 +- .../Controller/Adminhtml/Export/GridToXml.php | 2 +- .../Ui/Controller/Adminhtml/Index/Render.php | 2 +- .../Adminhtml/Index/Render/Handle.php | 2 +- .../Ui/Controller/UiActionInterface.php | 2 +- .../Ui/DataProvider/AbstractDataProvider.php | 2 +- .../Ui/DataProvider/AddFieldToCollection.php | 2 +- .../AddFieldToCollectionInterface.php | 2 +- .../AddFilterToCollectionInterface.php | 2 +- .../Ui/DataProvider/EavValidationRules.php | 2 +- .../Ui/DataProvider/Mapper/FormElement.php | 2 +- .../DataProvider/Mapper/MapperInterface.php | 2 +- .../Ui/DataProvider/Mapper/MetaProperties.php | 2 +- .../Ui/DataProvider/Modifier/Dummy.php | 2 +- .../DataProvider/Modifier/ModifierFactory.php | 2 +- .../Modifier/ModifierInterface.php | 2 +- .../Magento/Ui/DataProvider/Modifier/Pool.php | 2 +- .../DataProvider/Modifier/PoolInterface.php | 2 +- app/code/Magento/Ui/Model/Bookmark.php | 2 +- .../Magento/Ui/Model/BookmarkManagement.php | 2 +- app/code/Magento/Ui/Model/Config.php | 2 +- .../Magento/Ui/Model/Export/ConvertToCsv.php | 2 +- .../Magento/Ui/Model/Export/ConvertToXml.php | 2 +- .../Ui/Model/Export/MetadataProvider.php | 2 +- .../Ui/Model/Export/SearchResultIterator.php | 2 +- app/code/Magento/Ui/Model/Manager.php | 23 +- .../Ui/Model/ResourceModel/Bookmark.php | 2 +- .../ResourceModel/Bookmark/Collection.php | 2 +- .../ResourceModel/BookmarkRepository.php | 2 +- app/code/Magento/Ui/README.md | 2 +- app/code/Magento/Ui/Setup/InstallSchema.php | 2 +- .../Xhtml/Compiler/Element/Content.php | 2 +- .../Xhtml/Compiler/Element/Form.php | 2 +- .../Xhtml/Compiler/Element/Render.php | 2 +- .../Ui/TemplateEngine/Xhtml/Result.php | 2 +- .../Unit/Component/AbstractComponentTest.php | 2 +- .../Unit/Component/Control/ActionPoolTest.php | 2 +- .../Unit/Component/Control/ActionTest.php | 2 +- .../Unit/Component/Control/ButtonTest.php | 2 +- .../Unit/Component/Control/ContainerTest.php | 2 +- .../Test/Unit/Component/Control/LinkTest.php | 2 +- .../Test/Unit/Component/ExportButtonTest.php | 2 +- .../Component/Filters/FilterModifierTest.php | 2 +- .../Component/Filters/Type/DateRangeTest.php | 2 +- .../Unit/Component/Filters/Type/DateTest.php | 2 +- .../Unit/Component/Filters/Type/InputTest.php | 2 +- .../Unit/Component/Filters/Type/RangeTest.php | 2 +- .../Component/Filters/Type/SelectTest.php | 2 +- .../Form/Element/AbstractElementTest.php | 2 +- .../Form/Element/ActionDeleteTest.php | 2 +- .../Form/Element/CheckboxSetTest.php | 2 +- .../Form/Element/DataType/DateTest.php | 134 + .../Form/Element/DataType/MediaTest.php | 2 +- .../Form/Element/MultiSelectTest.php | 2 +- .../Component/Form/Element/RadioSetTest.php | 2 +- .../Component/Form/Element/SelectTest.php | 2 +- .../Component/Form/Element/WysiwygTest.php | 2 +- .../Component/Form/Field/MultilineTest.php | 2 +- .../Ui/Test/Unit/Component/Form/FieldTest.php | 2 +- .../Ui/Test/Unit/Component/FormTest.php | 2 +- .../Component/Listing/Columns/ColumnTest.php | 2 +- .../Component/Listing/Columns/DateTest.php | 2 +- .../Unit/Component/Listing/ColumnsTest.php | 2 +- .../Ui/Test/Unit/Component/ListingTest.php | 2 +- .../MassAction/Columns/ColumnTest.php | 2 +- .../Unit/Component/MassAction/FilterTest.php | 289 + .../Ui/Test/Unit/Component/MassActionTest.php | 2 +- .../Ui/Test/Unit/Component/PagingTest.php | 2 +- .../Adminhtml/Export/GridToCsvTest.php | 2 +- .../Adminhtml/Export/GridToXmlTest.php | 2 +- .../Adminhtml/Index/Render/HandleTest.php | 2 +- .../Controller/Adminhtml/Index/RenderTest.php | 2 +- .../DataProvider/EavValidationRulesTest.php | 2 +- .../Unit/DataProvider/Modifier/PoolTest.php | 2 +- .../Unit/Model/BookmarkManagementTest.php | 2 +- .../Unit/Model/Export/ConvertToCsvTest.php | 2 +- .../Unit/Model/Export/ConvertToXmlTest.php | 2 +- .../Model/Export/MetadataProviderTest.php | 2 +- .../Ui/Test/Unit/Model/ManagerTest.php | 28 +- .../ResourceModel/BookmarkRepositoryTest.php | 2 +- app/code/Magento/Ui/composer.json | 2 +- app/code/Magento/Ui/etc/adminhtml/di.xml | 4 +- app/code/Magento/Ui/etc/adminhtml/routes.xml | 2 +- app/code/Magento/Ui/etc/adminhtml/system.xml | 2 +- app/code/Magento/Ui/etc/config.xml | 2 +- app/code/Magento/Ui/etc/data_source.xsd | 2 +- app/code/Magento/Ui/etc/di.xml | 2 +- app/code/Magento/Ui/etc/module.xml | 2 +- app/code/Magento/Ui/etc/ui_components.xsd | 2 +- app/code/Magento/Ui/etc/ui_configuration.xsd | 2 +- app/code/Magento/Ui/etc/ui_definition.xsd | 2 +- app/code/Magento/Ui/etc/ui_template.xsd | 2 +- app/code/Magento/Ui/registration.php | 2 +- .../templates/modal/modal-prompt-content.html | 20 + .../Magento/Ui/view/base/layout/default.xml | 4 +- .../Magento/Ui/view/base/requirejs-config.js | 2 +- .../templates/container/content/default.phtml | 2 +- .../view/base/templates/context/default.phtml | 2 +- .../templates/control/button/default.phtml | 2 +- .../base/templates/control/button/split.phtml | 2 +- .../Ui/view/base/templates/form/default.phtml | 2 +- .../view/base/templates/label/default.phtml | 2 +- .../base/templates/layout/tabs/default.phtml | 2 +- .../templates/layout/tabs/nav/default.phtml | 2 +- .../Ui/view/base/templates/logger.phtml | 2 +- .../Ui/view/base/templates/stepswizard.phtml | 2 +- .../view/base/ui_component/etc/definition.xml | 2 +- .../templates/container/default.xhtml | 2 +- .../templates/export/button.xhtml | 4 +- .../templates/form/collapsible.xhtml | 2 +- .../ui_component/templates/form/default.xhtml | 2 +- .../templates/listing/default.xhtml | 2 +- .../Ui/view/base/web/js/block-loader.js | 3 +- .../Magento/Ui/view/base/web/js/core/app.js | 2 +- .../view/base/web/js/core/renderer/layout.js | 174 +- .../view/base/web/js/core/renderer/types.js | 20 +- .../base/web/js/dynamic-rows/action-delete.js | 2 +- .../Ui/view/base/web/js/dynamic-rows/dnd.js | 2 +- .../web/js/dynamic-rows/dynamic-rows-grid.js | 12 +- .../base/web/js/dynamic-rows/dynamic-rows.js | 5 +- .../view/base/web/js/dynamic-rows/record.js | 2 +- .../Ui/view/base/web/js/form/adapter.js | 5 +- .../view/base/web/js/form/button-adapter.js | 2 +- .../Ui/view/base/web/js/form/client.js | 8 +- .../view/base/web/js/form/components/area.js | 2 +- .../base/web/js/form/components/button.js | 2 +- .../base/web/js/form/components/collection.js | 9 +- .../web/js/form/components/collection/item.js | 2 +- .../base/web/js/form/components/fieldset.js | 2 +- .../view/base/web/js/form/components/group.js | 2 +- .../view/base/web/js/form/components/html.js | 4 +- .../web/js/form/components/insert-form.js | 2 +- .../web/js/form/components/insert-listing.js | 2 +- .../base/web/js/form/components/insert.js | 2 +- .../view/base/web/js/form/components/tab.js | 3 +- .../base/web/js/form/components/tab_group.js | 2 +- .../view/base/web/js/form/element/abstract.js | 7 +- .../view/base/web/js/form/element/boolean.js | 2 +- .../base/web/js/form/element/checkbox-set.js | 2 +- .../view/base/web/js/form/element/country.js | 3 +- .../Ui/view/base/web/js/form/element/date.js | 35 +- .../base/web/js/form/element/file-uploader.js | 35 +- .../Ui/view/base/web/js/form/element/media.js | 2 +- .../base/web/js/form/element/multiselect.js | 22 +- .../base/web/js/form/element/post-code.js | 2 +- .../view/base/web/js/form/element/region.js | 4 +- .../view/base/web/js/form/element/select.js | 54 +- .../element/single-checkbox-toggle-notice.js | 2 +- .../element/single-checkbox-use-config.js | 2 +- .../web/js/form/element/single-checkbox.js | 2 +- .../Ui/view/base/web/js/form/element/text.js | 2 +- .../view/base/web/js/form/element/textarea.js | 2 +- .../base/web/js/form/element/ui-select.js | 42 +- .../view/base/web/js/form/element/website.js | 2 +- .../view/base/web/js/form/element/wysiwyg.js | 4 +- .../Magento/Ui/view/base/web/js/form/form.js | 10 +- .../Ui/view/base/web/js/form/provider.js | 9 +- .../view/base/web/js/grid/columns/actions.js | 2 +- .../view/base/web/js/grid/columns/column.js | 2 +- .../Ui/view/base/web/js/grid/columns/date.js | 2 +- .../Ui/view/base/web/js/grid/columns/link.js | 2 +- .../base/web/js/grid/columns/multiselect.js | 2 +- .../Ui/view/base/web/js/grid/columns/onoff.js | 2 +- .../view/base/web/js/grid/columns/select.js | 24 +- .../base/web/js/grid/columns/thumbnail.js | 79 +- .../js/grid/controls/bookmarks/bookmarks.js | 2 +- .../web/js/grid/controls/bookmarks/storage.js | 10 +- .../view/base/web/js/grid/controls/columns.js | 2 +- .../Ui/view/base/web/js/grid/data-storage.js | 43 +- .../Magento/Ui/view/base/web/js/grid/dnd.js | 2 +- .../Ui/view/base/web/js/grid/editing/bulk.js | 2 +- .../view/base/web/js/grid/editing/client.js | 2 +- .../base/web/js/grid/editing/editor-view.js | 2 +- .../view/base/web/js/grid/editing/editor.js | 2 +- .../view/base/web/js/grid/editing/record.js | 2 +- .../Ui/view/base/web/js/grid/export.js | 29 +- .../Ui/view/base/web/js/grid/filters/chips.js | 2 +- .../view/base/web/js/grid/filters/filters.js | 2 +- .../Ui/view/base/web/js/grid/filters/range.js | 2 +- .../Ui/view/base/web/js/grid/listing.js | 2 +- .../Ui/view/base/web/js/grid/massactions.js | 2 +- .../Ui/view/base/web/js/grid/paging/paging.js | 2 +- .../Ui/view/base/web/js/grid/paging/sizes.js | 2 +- .../Ui/view/base/web/js/grid/provider.js | 12 +- .../Ui/view/base/web/js/grid/resize.js | 17 +- .../Ui/view/base/web/js/grid/search/search.js | 2 +- .../Ui/view/base/web/js/grid/sticky/sticky.js | 2 +- .../Ui/view/base/web/js/grid/toolbar.js | 2 +- .../view/base/web/js/grid/tree-massactions.js | 2 +- .../Ui/view/base/web/js/lib/collapsible.js | 2 +- .../Ui/view/base/web/js/lib/core/class.js | 2 +- .../view/base/web/js/lib/core/collection.js | 10 +- .../base/web/js/lib/core/element/element.js | 2 +- .../base/web/js/lib/core/element/links.js | 88 +- .../Ui/view/base/web/js/lib/core/events.js | 2 +- .../base/web/js/lib/core/storage/local.js | 2 +- .../Ui/view/base/web/js/lib/key-codes.js | 2 +- .../js/lib/knockout/bindings/after-render.js | 2 +- .../js/lib/knockout/bindings/autoselect.js | 2 +- .../web/js/lib/knockout/bindings/bind-html.js | 2 +- .../web/js/lib/knockout/bindings/bootstrap.js | 2 +- .../js/lib/knockout/bindings/collapsible.js | 2 +- .../js/lib/knockout/bindings/datepicker.js | 20 +- .../js/lib/knockout/bindings/fadeVisible.js | 29 +- .../base/web/js/lib/knockout/bindings/i18n.js | 2 +- .../web/js/lib/knockout/bindings/keyboard.js | 2 +- .../web/js/lib/knockout/bindings/mage-init.js | 9 +- .../web/js/lib/knockout/bindings/optgroup.js | 116 +- .../js/lib/knockout/bindings/outer_click.js | 2 +- .../web/js/lib/knockout/bindings/range.js | 2 +- .../web/js/lib/knockout/bindings/resizable.js | 2 +- .../web/js/lib/knockout/bindings/scope.js | 2 +- .../lib/knockout/bindings/simple-checked.js | 2 +- .../js/lib/knockout/bindings/staticChecked.js | 2 +- .../web/js/lib/knockout/bindings/tooltip.js | 2 +- .../base/web/js/lib/knockout/bootstrap.js | 2 +- .../js/lib/knockout/extender/bound-nodes.js | 2 +- .../lib/knockout/extender/observable_array.js | 64 +- .../web/js/lib/knockout/template/engine.js | 2 +- .../web/js/lib/knockout/template/loader.js | 2 +- .../knockout/template/observable_source.js | 2 +- .../web/js/lib/knockout/template/renderer.js | 2 +- .../view/base/web/js/lib/registry/registry.js | 2 +- .../Ui/view/base/web/js/lib/spinner.js | 15 +- .../Ui/view/base/web/js/lib/step-wizard.js | 131 +- .../view/base/web/js/lib/validation/rules.js | 623 +- .../view/base/web/js/lib/validation/utils.js | 69 +- .../base/web/js/lib/validation/validator.js | 2 +- .../view/base/web/js/lib/view/utils/async.js | 2 +- .../base/web/js/lib/view/utils/bindings.js | 2 +- .../web/js/lib/view/utils/dom-observer.js | 2 +- .../Ui/view/base/web/js/lib/view/utils/raf.js | 2 +- .../Ui/view/base/web/js/modal/alert.js | 2 +- .../Ui/view/base/web/js/modal/confirm.js | 2 +- .../view/base/web/js/modal/modal-component.js | 2 +- .../Ui/view/base/web/js/modal/modal.js | 4 +- .../Ui/view/base/web/js/modal/modalToggle.js | 2 +- .../Ui/view/base/web/js/modal/prompt.js | 88 +- .../base/web/js/timeline/timeline-view.js | 2 +- .../Ui/view/base/web/js/timeline/timeline.js | 2 +- .../Ui/view/base/web/templates/area.html | 4 +- .../view/base/web/templates/block-loader.html | 4 +- .../view/base/web/templates/collection.html | 2 +- .../base/web/templates/content/content.html | 4 +- .../dynamic-rows/cells/action-delete.html | 4 +- .../web/templates/dynamic-rows/cells/dnd.html | 2 +- .../templates/dynamic-rows/cells/text.html | 4 +- .../dynamic-rows/cells/thumbnail.html | 4 +- .../dynamic-rows/templates/collapsible.html | 3 +- .../dynamic-rows/templates/default.html | 2 +- .../dynamic-rows/templates/grid.html | 4 +- .../base/web/templates/form/collection.html | 2 +- .../form/components/button/container.html | 2 +- .../form/components/button/simple.html | 4 +- .../templates/form/components/collection.html | 2 +- .../form/components/collection/preview.html | 4 +- .../templates/form/components/complex.html | 2 +- .../form/components/single/checkbox.html | 2 +- .../form/components/single/field.html | 2 +- .../form/components/single/radio.html | 2 +- .../form/components/single/switcher.html | 2 +- .../web/templates/form/element/button.html | 4 +- .../templates/form/element/checkbox-set.html | 4 +- .../web/templates/form/element/checkbox.html | 2 +- .../base/web/templates/form/element/date.html | 2 +- .../web/templates/form/element/email.html | 2 +- .../form/element/helper/fallback-reset.html | 2 +- .../form/element/helper/service.html | 2 +- .../form/element/helper/tooltip.html | 2 +- .../web/templates/form/element/hidden.html | 2 +- .../web/templates/form/element/input.html | 2 +- .../web/templates/form/element/media.html | 2 +- .../templates/form/element/multiselect.html | 2 +- .../web/templates/form/element/preview.html | 2 +- .../web/templates/form/element/price.html | 4 +- .../web/templates/form/element/radio.html | 2 +- .../web/templates/form/element/select.html | 2 +- .../templates/form/element/split-button.html | 4 +- .../web/templates/form/element/switcher.html | 2 +- .../base/web/templates/form/element/text.html | 2 +- .../web/templates/form/element/textDate.html | 2 +- .../web/templates/form/element/textarea.html | 2 +- .../form/element/uploader/preview.html | 13 +- .../form/element/uploader/uploader.html | 15 +- .../web/templates/form/element/wysiwyg.html | 2 +- .../view/base/web/templates/form/field.html | 2 +- .../base/web/templates/form/fieldset.html | 2 +- .../view/base/web/templates/form/insert.html | 2 +- .../view/base/web/templates/grid/actions.html | 2 +- .../web/templates/grid/cells/actions.html | 2 +- .../base/web/templates/grid/cells/html.html | 4 +- .../base/web/templates/grid/cells/link.html | 2 +- .../web/templates/grid/cells/multiselect.html | 4 +- .../base/web/templates/grid/cells/onoff.html | 2 +- .../base/web/templates/grid/cells/text.html | 4 +- .../web/templates/grid/cells/thumbnail.html | 4 +- .../grid/cells/thumbnail/preview.html | 2 +- .../templates/grid/columns/multiselect.html | 4 +- .../web/templates/grid/columns/onoff.html | 2 +- .../base/web/templates/grid/columns/text.html | 2 +- .../grid/controls/bookmarks/bookmarks.html | 2 +- .../grid/controls/bookmarks/view.html | 4 +- .../web/templates/grid/controls/columns.html | 4 +- .../base/web/templates/grid/editing/bulk.html | 2 +- .../web/templates/grid/editing/field.html | 2 +- .../grid/editing/header-buttons.html | 2 +- .../templates/grid/editing/row-buttons.html | 4 +- .../base/web/templates/grid/editing/row.html | 2 +- .../base/web/templates/grid/exportButton.html | 4 +- .../web/templates/grid/filters/chips.html | 4 +- .../grid/filters/elements/group.html | 2 +- .../filters/elements/ui-select-optgroup.html | 4 +- .../grid/filters/elements/ui-select.html | 4 +- .../web/templates/grid/filters/field.html | 2 +- .../web/templates/grid/filters/filters.html | 2 +- .../view/base/web/templates/grid/listing.html | 4 +- .../base/web/templates/grid/paging-total.html | 2 +- .../grid/paging/paging-detailed-total.html | 2 +- .../web/templates/grid/paging/paging.html | 2 +- .../base/web/templates/grid/paging/sizes.html | 4 +- .../web/templates/grid/search/search.html | 2 +- .../web/templates/grid/sticky/filters.html | 2 +- .../web/templates/grid/sticky/listing.html | 4 +- .../web/templates/grid/sticky/sticky.html | 2 +- .../view/base/web/templates/grid/submenu.html | 2 +- .../view/base/web/templates/grid/toolbar.html | 2 +- .../web/templates/grid/tree-massactions.html | 2 +- .../web/templates/grid/view-switcher.html | 2 +- .../view/base/web/templates/group/group.html | 2 +- .../web/templates/modal/modal-component.html | 2 +- .../web/templates/modal/modal-custom.html | 2 +- .../base/web/templates/modal/modal-popup.html | 2 +- .../templates/modal/modal-prompt-content.html | 20 + .../base/web/templates/modal/modal-slide.html | 2 +- .../Ui/view/base/web/templates/tab.html | 4 +- .../base/web/templates/timeline/record.html | 2 +- .../base/web/templates/timeline/timeline.html | 2 +- .../base/web/templates/tooltip/tooltip.html | 4 +- .../view/frontend/web/js/model/messageList.js | 18 +- .../Ui/view/frontend/web/js/model/messages.js | 44 +- .../Ui/view/frontend/web/js/view/messages.js | 19 +- .../view/frontend/web/template/messages.html | 2 +- .../web/templates/form/element/checkbox.html | 2 +- .../web/templates/form/element/date.html | 2 +- .../web/templates/form/element/email.html | 2 +- .../form/element/helper/tooltip.html | 2 +- .../web/templates/form/element/input.html | 2 +- .../web/templates/form/element/password.html | 2 +- .../web/templates/form/element/select.html | 2 +- .../frontend/web/templates/form/field.html | 2 +- .../frontend/web/templates/group/group.html | 2 +- .../Block/Backend/System/CarrierConfig.php | 2 +- app/code/Magento/Ups/Helper/Config.php | 2 +- app/code/Magento/Ups/Model/Carrier.php | 2 +- .../Ups/Model/Config/Source/Container.php | 2 +- .../Ups/Model/Config/Source/DestType.php | 2 +- .../Ups/Model/Config/Source/Freemethod.php | 2 +- .../Ups/Model/Config/Source/Generic.php | 2 +- .../Ups/Model/Config/Source/Method.php | 2 +- .../Model/Config/Source/OriginShipment.php | 2 +- .../Ups/Model/Config/Source/Pickup.php | 2 +- .../Magento/Ups/Model/Config/Source/Type.php | 2 +- .../Ups/Model/Config/Source/Unitofmeasure.php | 2 +- .../Ups/Test/Unit/Helper/ConfigTest.php | 2 +- .../Ups/Test/Unit/Model/CarrierTest.php | 2 +- app/code/Magento/Ups/composer.json | 5 +- app/code/Magento/Ups/etc/adminhtml/system.xml | 2 +- app/code/Magento/Ups/etc/config.xml | 2 +- app/code/Magento/Ups/etc/di.xml | 21 + app/code/Magento/Ups/etc/module.xml | 2 +- app/code/Magento/Ups/registration.php | 2 +- .../layout/adminhtml_system_config_edit.xml | 2 +- .../system/shipping/carrier_config.phtml | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../model/shipping-rates-validation-rules.js | 41 +- .../web/js/model/shipping-rates-validator.js | 58 +- .../web/js/view/shipping-rates-validation.js | 45 +- .../Block/Catalog/Category/Edit.php | 2 +- .../Block/Catalog/Category/Tree.php | 2 +- .../UrlRewrite/Block/Catalog/Edit/Form.php | 2 +- .../UrlRewrite/Block/Catalog/Product/Edit.php | 2 +- .../UrlRewrite/Block/Catalog/Product/Grid.php | 2 +- .../UrlRewrite/Block/Cms/Page/Edit.php | 2 +- .../UrlRewrite/Block/Cms/Page/Edit/Form.php | 2 +- .../UrlRewrite/Block/Cms/Page/Grid.php | 2 +- app/code/Magento/UrlRewrite/Block/Edit.php | 2 +- .../Magento/UrlRewrite/Block/Edit/Form.php | 2 +- .../UrlRewrite/Block/GridContainer.php | 2 +- app/code/Magento/UrlRewrite/Block/Link.php | 2 +- .../Plugin/Store/Switcher/SetRedirectUrl.php | 2 +- .../Magento/UrlRewrite/Block/Selector.php | 2 +- .../Controller/Adminhtml/Url/Rewrite.php | 2 +- .../Adminhtml/Url/Rewrite/CategoriesJson.php | 2 +- .../Adminhtml/Url/Rewrite/CmsPageGrid.php | 2 +- .../Adminhtml/Url/Rewrite/Delete.php | 2 +- .../Controller/Adminhtml/Url/Rewrite/Edit.php | 2 +- .../Adminhtml/Url/Rewrite/Index.php | 2 +- .../Adminhtml/Url/Rewrite/ProductGrid.php | 2 +- .../Controller/Adminhtml/Url/Rewrite/Save.php | 6 +- .../Magento/UrlRewrite/Controller/Router.php | 2 +- .../Magento/UrlRewrite/Helper/UrlRewrite.php | 2 +- .../UrlRewrite/Model/MergeDataProvider.php | 51 + .../UrlRewrite/Model/OptionProvider.php | 2 +- .../Model/ResourceModel/UrlRewrite.php | 2 +- .../ResourceModel/UrlRewriteCollection.php | 2 +- .../Model/Storage/AbstractStorage.php | 2 +- .../UrlRewrite/Model/Storage/DbStorage.php | 2 +- .../UrlRewrite/Model/StorageInterface.php | 2 +- .../UrlRewrite/Model/UrlFinderInterface.php | 2 +- .../UrlRewrite/Model/UrlPersistInterface.php | 2 +- .../Magento/UrlRewrite/Model/UrlRewrite.php | 49 +- .../UrlRewrite/Service/V1/Data/UrlRewrite.php | 27 +- .../UrlRewrite/Setup/InstallSchema.php | 2 +- .../Magento/UrlRewrite/Setup/UpgradeData.php | 63 + .../Test/Unit/Block/Catalog/Edit/FormTest.php | 2 +- .../Store/Switcher/SetRedirectUrlTest.php | 4 +- .../Test/Unit/Controller/RouterTest.php | 2 +- .../Test/Unit/Helper/UrlRewriteTest.php | 2 +- .../Test/Unit/Model/MergeDataProviderTest.php | 129 + .../UrlRewriteCollectionTest.php | 2 +- .../Model/Storage/AbstractStorageTest.php | 2 +- .../Test/Unit/Model/Storage/DbStorageTest.php | 2 +- .../Test/Unit/Model/UrlRewriteTest.php | 66 + .../Unit/Service/V1/Data/UrlRewriteTest.php | 52 + app/code/Magento/UrlRewrite/composer.json | 2 +- app/code/Magento/UrlRewrite/etc/acl.xml | 2 +- .../Magento/UrlRewrite/etc/adminhtml/menu.xml | 2 +- .../UrlRewrite/etc/adminhtml/routes.xml | 2 +- app/code/Magento/UrlRewrite/etc/config.xml | 2 +- app/code/Magento/UrlRewrite/etc/di.xml | 2 +- .../Magento/UrlRewrite/etc/frontend/di.xml | 2 +- app/code/Magento/UrlRewrite/etc/module.xml | 4 +- app/code/Magento/UrlRewrite/registration.php | 2 +- .../layout/adminhtml_url_rewrite_index.xml | 2 +- .../view/adminhtml/templates/categories.phtml | 4 +- .../view/adminhtml/templates/edit.phtml | 2 +- .../view/adminhtml/templates/selector.phtml | 2 +- .../Magento/User/Api/Data/UserInterface.php | 2 +- .../Magento/User/Block/Adminhtml/Locks.php | 2 +- app/code/Magento/User/Block/Buttons.php | 2 +- app/code/Magento/User/Block/Role.php | 2 +- app/code/Magento/User/Block/Role/Edit.php | 2 +- .../Magento/User/Block/Role/Grid/User.php | 2 +- app/code/Magento/User/Block/Role/Tab/Edit.php | 2 +- app/code/Magento/User/Block/Role/Tab/Info.php | 2 +- .../Magento/User/Block/Role/Tab/Users.php | 2 +- app/code/Magento/User/Block/User.php | 2 +- app/code/Magento/User/Block/User/Edit.php | 51 +- .../Magento/User/Block/User/Edit/Form.php | 2 +- .../Magento/User/Block/User/Edit/Tab/Main.php | 2 +- .../User/Block/User/Edit/Tab/Roles.php | 2 +- .../Magento/User/Block/User/Edit/Tabs.php | 2 +- .../Console/UnlockAdminAccountCommand.php | 2 +- .../User/Controller/Adminhtml/Auth.php | 2 +- .../Adminhtml/Auth/Forgotpassword.php | 2 +- .../Adminhtml/Auth/ResetPassword.php | 2 +- .../Adminhtml/Auth/ResetPasswordPost.php | 2 +- .../User/Controller/Adminhtml/Locks.php | 2 +- .../User/Controller/Adminhtml/Locks/Grid.php | 2 +- .../User/Controller/Adminhtml/Locks/Index.php | 2 +- .../Controller/Adminhtml/Locks/MassUnlock.php | 2 +- .../User/Controller/Adminhtml/User.php | 2 +- .../User/Controller/Adminhtml/User/Delete.php | 12 +- .../User/Controller/Adminhtml/User/Edit.php | 2 +- .../User/Controller/Adminhtml/User/Index.php | 2 +- .../Adminhtml/User/InvalidateToken.php | 2 +- .../Controller/Adminhtml/User/NewAction.php | 2 +- .../User/Controller/Adminhtml/User/Role.php | 2 +- .../Controller/Adminhtml/User/Role/Delete.php | 2 +- .../Adminhtml/User/Role/EditRole.php | 2 +- .../Adminhtml/User/Role/Editrolegrid.php | 9 +- .../Controller/Adminhtml/User/Role/Index.php | 2 +- .../Adminhtml/User/Role/RoleGrid.php | 2 +- .../Adminhtml/User/Role/SaveRole.php | 2 +- .../Controller/Adminhtml/User/RoleGrid.php | 2 +- .../Controller/Adminhtml/User/RolesGrid.php | 2 +- .../User/Controller/Adminhtml/User/Save.php | 2 +- .../Controller/Adminhtml/User/Validate.php | 2 +- app/code/Magento/User/Helper/Data.php | 2 +- .../Authorization/AdminSessionUserContext.php | 2 +- .../Model/Backend/Config/ObserverConfig.php | 2 +- .../User/Model/Plugin/AuthorizationRole.php | 2 +- .../ResourceModel/Role/User/Collection.php | 2 +- .../Magento/User/Model/ResourceModel/User.php | 26 +- .../Model/ResourceModel/User/Collection.php | 2 +- .../ResourceModel/User/Locked/Collection.php | 2 +- .../Model/System/Config/Source/Password.php | 2 +- app/code/Magento/User/Model/User.php | 21 +- .../User/Model/UserValidationRules.php | 2 +- .../User/Observer/Backend/AuthObserver.php | 2 +- .../ForceAdminPasswordChangeObserver.php | 2 +- .../Backend/TrackAdminNewPasswordObserver.php | 2 +- app/code/Magento/User/Setup/InstallSchema.php | 2 +- app/code/Magento/User/Setup/UpgradeData.php | 42 +- app/code/Magento/User/Setup/UpgradeSchema.php | 2 +- .../User/Test/Unit/Block/Role/EditTest.php | 2 +- .../Test/Unit/Block/Role/Grid/UserTest.php | 2 +- .../Test/Unit/Block/Role/Tab/EditTest.php | 2 +- .../Test/Unit/Block/Role/Tab/InfoTest.php | 2 +- .../Test/Unit/Block/Role/Tab/UsersTest.php | 2 +- .../Console/UnlockAdminAccountCommandTest.php | 2 +- .../Controller/Adminhtml/User/DeleteTest.php | 215 + .../User/Test/Unit/Helper/DataTest.php | 2 +- .../AdminSessionUserContextTest.php | 2 +- .../Model/Plugin/AuthorizationRoleTest.php | 2 +- .../Unit/Model/ResourceModel/UserTest.php | 102 +- .../Magento/User/Test/Unit/Model/UserTest.php | 25 +- .../Unit/Model/UserValidationRulesTest.php | 2 +- .../Observer/Backend/AuthObserverTest.php | 2 +- .../ForceAdminPasswordChangeObserverTest.php | 2 +- .../TrackAdminNewPasswordObserverTest.php | 2 +- app/code/Magento/User/composer.json | 2 +- app/code/Magento/User/etc/acl.xml | 2 +- app/code/Magento/User/etc/adminhtml/di.xml | 2 +- .../Magento/User/etc/adminhtml/events.xml | 2 +- app/code/Magento/User/etc/adminhtml/menu.xml | 2 +- .../Magento/User/etc/adminhtml/routes.xml | 2 +- .../Magento/User/etc/adminhtml/system.xml | 2 +- app/code/Magento/User/etc/config.xml | 2 +- app/code/Magento/User/etc/di.xml | 2 +- app/code/Magento/User/etc/email_templates.xml | 2 +- app/code/Magento/User/etc/module.xml | 4 +- app/code/Magento/User/etc/webapi_rest/di.xml | 2 +- app/code/Magento/User/registration.php | 2 +- .../email/password_reset_confirmation.html | 2 +- .../adminhtml/email/user_notification.html | 2 +- .../layout/adminhtml_auth_forgotpassword.xml | 2 +- .../adminhtml/layout/adminhtml_auth_login.xml | 2 +- .../layout/adminhtml_auth_resetpassword.xml | 4 +- .../layout/adminhtml_locks_block.xml | 2 +- .../adminhtml/layout/adminhtml_locks_grid.xml | 2 +- .../layout/adminhtml_locks_index.xml | 2 +- .../adminhtml/layout/adminhtml_user_edit.xml | 2 +- .../layout/adminhtml_user_grid_block.xml | 2 +- .../adminhtml/layout/adminhtml_user_index.xml | 2 +- .../layout/adminhtml_user_role_editrole.xml | 2 +- .../adminhtml_user_role_editrolegrid.xml | 2 +- .../layout/adminhtml_user_role_grid_block.xml | 2 +- .../layout/adminhtml_user_role_index.xml | 2 +- .../layout/adminhtml_user_role_rolegrid.xml | 2 +- .../layout/adminhtml_user_rolegrid.xml | 2 +- .../layout/adminhtml_user_rolesgrid.xml | 2 +- .../User/view/adminhtml/requirejs-config.js | 9 +- .../templates/admin/forgotpassword.phtml | 2 +- .../templates/admin/forgotpassword_url.phtml | 2 +- .../admin/resetforgottenpassword.phtml | 2 +- .../view/adminhtml/templates/role/edit.phtml | 2 +- .../view/adminhtml/templates/role/info.phtml | 2 +- .../view/adminhtml/templates/role/users.phtml | 2 +- .../templates/role/users_grid_js.phtml | 2 +- .../templates/user/roles_grid_js.phtml | 17 +- .../User/view/adminhtml/web/app-config.js | 53 +- .../adminhtml/web/js/delete-user-account.js | 28 + .../User/view/adminhtml/web/js/roles-tree.js | 83 +- .../Order/Packaging/Plugin/DisplayGirth.php | 2 +- app/code/Magento/Usps/Helper/Data.php | 2 +- app/code/Magento/Usps/Model/Carrier.php | 251 +- .../Magento/Usps/Model/Source/Container.php | 2 +- .../Magento/Usps/Model/Source/Freemethod.php | 2 +- .../Magento/Usps/Model/Source/Generic.php | 2 +- .../Magento/Usps/Model/Source/Machinable.php | 2 +- app/code/Magento/Usps/Model/Source/Method.php | 2 +- app/code/Magento/Usps/Model/Source/Size.php | 2 +- app/code/Magento/Usps/Setup/InstallData.php | 2 +- .../Usps/Test/Unit/Helper/DataTest.php | 2 +- .../Usps/Test/Unit/Model/CarrierTest.php | 2 +- .../Test/Unit/Model/Source/GenericTest.php | 2 +- .../Unit/Model/_files/rates_request_data.php | 2 +- .../_files/return_shipment_request_data.php | 2 +- .../_files/success_usps_response_rates.xml | 2 +- .../success_usps_response_return_shipment.xml | 2 +- app/code/Magento/Usps/composer.json | 2 +- app/code/Magento/Usps/etc/adminhtml/di.xml | 2 +- .../Magento/Usps/etc/adminhtml/system.xml | 2 +- app/code/Magento/Usps/etc/config.xml | 2 +- app/code/Magento/Usps/etc/di.xml | 19 + app/code/Magento/Usps/etc/module.xml | 2 +- app/code/Magento/Usps/registration.php | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../model/shipping-rates-validation-rules.js | 41 +- .../web/js/model/shipping-rates-validator.js | 77 +- .../web/js/view/shipping-rates-validation.js | 46 +- .../Variable/Block/System/Variable.php | 2 +- .../Variable/Block/System/Variable/Edit.php | 2 +- .../Block/System/Variable/Edit/Form.php | 2 +- .../Controller/Adminhtml/System/Variable.php | 2 +- .../Adminhtml/System/Variable/Delete.php | 2 +- .../Adminhtml/System/Variable/Edit.php | 2 +- .../Adminhtml/System/Variable/Index.php | 2 +- .../Adminhtml/System/Variable/NewAction.php | 2 +- .../Adminhtml/System/Variable/Save.php | 2 +- .../Adminhtml/System/Variable/Validate.php | 2 +- .../System/Variable/WysiwygPlugin.php | 2 +- .../Variable/Model/ResourceModel/Variable.php | 2 +- .../ResourceModel/Variable/Collection.php | 2 +- app/code/Magento/Variable/Model/Variable.php | 2 +- .../Variable/Model/Variable/Config.php | 2 +- .../Magento/Variable/Setup/InstallSchema.php | 2 +- .../System/Variable/ValidateTest.php | 2 +- .../Test/Unit/Model/Variable/ConfigTest.php | 2 +- .../Variable/Test/Unit/Model/VariableTest.php | 2 +- app/code/Magento/Variable/composer.json | 2 +- app/code/Magento/Variable/etc/acl.xml | 2 +- .../Magento/Variable/etc/adminhtml/menu.xml | 2 +- .../Magento/Variable/etc/adminhtml/routes.xml | 4 +- app/code/Magento/Variable/etc/module.xml | 2 +- app/code/Magento/Variable/registration.php | 2 +- .../layout/adminhtml_system_variable_edit.xml | 2 +- .../adminhtml_system_variable_grid_block.xml | 2 +- .../adminhtml_system_variable_index.xml | 2 +- .../templates/system/variable/js.phtml | 2 +- .../Variable/view/adminhtml/web/variables.js | 311 +- .../Vault/Api/Data/PaymentTokenInterface.php | 2 +- .../Api/Data/PaymentTokenInterfaceFactory.php | 4 +- .../PaymentTokenSearchResultsInterface.php | 2 +- .../Vault/Api/PaymentMethodListInterface.php | 30 + .../Api/PaymentTokenManagementInterface.php | 2 +- .../Api/PaymentTokenRepositoryInterface.php | 2 +- .../Vault/Block/AbstractCardRenderer.php | 2 +- .../Vault/Block/AbstractTokenRenderer.php | 2 +- .../Vault/Block/CardRendererInterface.php | 2 +- .../Vault/Block/Customer/AccountTokens.php | 2 +- .../Vault/Block/Customer/CreditCards.php | 2 +- .../Vault/Block/Customer/IconInterface.php | 4 +- .../Vault/Block/Customer/PaymentTokens.php | 2 +- app/code/Magento/Vault/Block/Form.php | 2 +- .../Vault/Block/TokenRendererInterface.php | 2 +- .../Vault/Controller/Cards/DeleteAction.php | 2 +- .../Vault/Controller/Cards/ListAction.php | 2 +- .../Vault/Controller/CardsManagement.php | 2 +- .../Model/AbstractPaymentTokenFactory.php | 2 +- .../Model/AccountPaymentTokenFactory.php | 2 +- .../Vault/Model/CreditCardTokenFactory.php | 2 +- .../Vault/Model/CustomerTokenManagement.php | 2 +- .../Model/Method/NullPaymentProvider.php | 2 +- app/code/Magento/Vault/Model/Method/Vault.php | 24 +- .../Magento/Vault/Model/PaymentMethodList.php | 78 + app/code/Magento/Vault/Model/PaymentToken.php | 2 +- .../Vault/Model/PaymentTokenManagement.php | 2 +- .../Vault/Model/PaymentTokenRepository.php | 3 +- .../Model/ResourceModel/PaymentToken.php | 2 +- .../ResourceModel/PaymentToken/Collection.php | 2 +- .../Ui/Adminhtml/TokensConfigProvider.php | 12 +- .../Vault/Model/Ui/TokenUiComponent.php | 2 +- .../Model/Ui/TokenUiComponentInterface.php | 2 +- .../Ui/TokenUiComponentProviderInterface.php | 2 +- .../Vault/Model/Ui/TokensConfigProvider.php | 82 +- .../Vault/Model/Ui/VaultConfigProvider.php | 75 +- .../Vault/Model/VaultPaymentInterface.php | 2 +- .../Observer/AfterPaymentSaveObserver.php | 2 +- .../Vault/Observer/PaymentTokenAssigner.php | 2 +- .../Vault/Observer/VaultEnableAssigner.php | 2 +- .../Plugin/PaymentVaultAttributesLoad.php | 2 +- .../PaymentVaultConfigurationProcess.php | 93 + .../Magento/Vault/Setup/InstallSchema.php | 2 +- app/code/Magento/Vault/Setup/UpgradeData.php | 22 +- .../Unit/Block/Customer/AccountTokensTest.php | 2 +- .../Model/AccountPaymentTokenFactoryTest.php | 2 +- .../Unit/Model/CreditCardTokenFactoryTest.php | 2 +- .../Model/CustomerTokenManagementTest.php | 2 +- .../Test/Unit/Model/Method/VaultTest.php | 84 +- .../Test/Unit/Model/PaymentMethodListTest.php | 74 + .../Unit/Model/PaymentTokenManagementTest.php | 2 +- .../Unit/Model/PaymentTokenRepositoryTest.php | 2 +- .../Ui/Adminhtml/TokensConfigProviderTest.php | 210 +- .../Model/Ui/TokensConfigProviderTest.php | 60 +- .../Unit/Model/Ui/VaultConfigProviderTest.php | 62 +- .../Observer/AfterPaymentSaveObserverTest.php | 2 +- .../Observer/PaymentTokenAssignerTest.php | 2 +- .../Unit/Observer/VaultEnableAssignerTest.php | 2 +- .../PaymentVaultConfigurationProcessTest.php | 158 + app/code/Magento/Vault/composer.json | 2 +- app/code/Magento/Vault/etc/adminhtml/di.xml | 4 +- app/code/Magento/Vault/etc/config.xml | 2 +- app/code/Magento/Vault/etc/di.xml | 3 +- app/code/Magento/Vault/etc/events.xml | 2 +- .../Vault/etc/extension_attributes.xml | 2 +- app/code/Magento/Vault/etc/frontend/di.xml | 6 +- .../Magento/Vault/etc/frontend/routes.xml | 2 +- app/code/Magento/Vault/etc/module.xml | 2 +- app/code/Magento/Vault/registration.php | 2 +- .../view/adminhtml/templates/form/vault.phtml | 2 +- .../Vault/view/adminhtml/web/js/vault.js | 20 +- .../frontend/layout/checkout_index_index.xml | 2 +- .../view/frontend/layout/customer_account.xml | 5 +- .../layout/vault_cards_listaction.xml | 2 +- .../view/frontend/templates/cards_list.phtml | 2 +- .../customer_account/credit_card.phtml | 2 +- .../view/frontend/templates/token_list.phtml | 2 +- .../web/js/customer_account/deleteWidget.js | 2 +- .../js/view/payment/method-renderer/vault.js | 2 +- .../web/js/view/payment/vault-enabler.js | 2 +- .../frontend/web/js/view/payment/vault.js | 48 +- .../frontend/web/template/payment/form.html | 4 +- .../Version/Controller/Index/Index.php | 2 +- app/code/Magento/Version/composer.json | 2 +- .../Magento/Version/etc/frontend/routes.xml | 2 +- app/code/Magento/Version/etc/module.xml | 2 +- app/code/Magento/Version/registration.php | 2 +- .../Webapi/Controller/PathProcessor.php | 2 +- app/code/Magento/Webapi/Controller/Rest.php | 5 +- .../Controller/Rest/InputParamsResolver.php | 2 +- .../Rest/ParamOverriderCustomerId.php | 2 +- .../Controller/Rest/ParamsOverrider.php | 2 +- .../Controller/Rest/RequestValidator.php | 2 +- .../Magento/Webapi/Controller/Rest/Router.php | 2 +- .../Webapi/Controller/Rest/Router/Route.php | 2 +- app/code/Magento/Webapi/Controller/Soap.php | 2 +- .../Controller/Soap/Request/Handler.php | 2 +- .../Webapi/Model/AbstractSchemaGenerator.php | 2 +- .../Model/Authorization/GuestUserContext.php | 2 +- .../Model/Authorization/OauthUserContext.php | 2 +- .../Model/Authorization/TokenUserContext.php | 2 +- .../Webapi/Model/Cache/Type/Webapi.php | 2 +- app/code/Magento/Webapi/Model/Config.php | 22 +- .../Webapi/Model/Config/ClassReflector.php | 2 +- .../Magento/Webapi/Model/Config/Converter.php | 2 +- .../Magento/Webapi/Model/Config/Reader.php | 2 +- .../Webapi/Model/Config/SchemaLocator.php | 2 +- .../Model/Plugin/GuestAuthorization.php | 2 +- .../Magento/Webapi/Model/Plugin/Manager.php | 2 +- app/code/Magento/Webapi/Model/Rest/Config.php | 2 +- .../Magento/Webapi/Model/Rest/Swagger.php | 2 +- .../Webapi/Model/Rest/Swagger/Generator.php | 2 +- .../Magento/Webapi/Model/ServiceMetadata.php | 42 +- app/code/Magento/Webapi/Model/Soap/Config.php | 2 +- app/code/Magento/Webapi/Model/Soap/Fault.php | 2 +- app/code/Magento/Webapi/Model/Soap/Server.php | 2 +- .../Webapi/Model/Soap/ServerFactory.php | 2 +- app/code/Magento/Webapi/Model/Soap/Wsdl.php | 2 +- .../Model/Soap/Wsdl/ComplexTypeStrategy.php | 2 +- .../Webapi/Model/Soap/Wsdl/Generator.php | 2 +- .../Magento/Webapi/Model/Soap/WsdlFactory.php | 2 +- .../Webapi/Model/WebapiRoleLocator.php | 2 +- .../Unit/Controller/PathProcessorTest.php | 2 +- .../Rest/ParamOverriderCustomerIdTest.php | 2 +- .../Controller/Rest/ParamsOverriderTest.php | 2 +- .../Controller/Rest/RequestValidatorTest.php | 2 +- .../Unit/Controller/Rest/Router/RouteTest.php | 2 +- .../Test/Unit/Controller/Rest/RouterTest.php | 2 +- .../Webapi/Test/Unit/Controller/RestTest.php | 2 +- .../Controller/Soap/Request/HandlerTest.php | 2 +- .../Webapi/Test/Unit/Controller/SoapTest.php | 2 +- .../Webapi/Test/Unit/ExceptionTest.php | 2 +- .../Authorization/GuestUserContextTest.php | 2 +- .../Authorization/OauthUserContextTest.php | 2 +- .../Authorization/TokenUserContextTest.php | 2 +- .../Unit/Model/Config/ClassReflectorTest.php | 2 +- .../Test/Unit/Model/Config/ConverterTest.php | 2 +- .../Config/TestServiceForClassReflector.php | 2 +- .../Test/Unit/Model/Config/_files/webapi.php | 2 +- .../Test/Unit/Model/Config/_files/webapi.xml | 2 +- .../Webapi/Test/Unit/Model/ConfigTest.php | 96 + .../Unit/Model/DataObjectProcessorTest.php | 15 +- .../Unit/Model/Files/TestDataInterface.php | 2 +- .../Test/Unit/Model/Files/TestDataObject.php | 2 +- .../Test/Unit/Model/Plugin/ManagerTest.php | 2 +- .../Unit/Model/Rest/Swagger/GeneratorTest.php | 2 +- .../Test/Unit/Model/ServiceMetadataTest.php | 600 +- .../Test/Unit/Model/Soap/ConfigTest.php | 166 - .../Webapi/Test/Unit/Model/Soap/FaultTest.php | 2 +- .../Test/Unit/Model/Soap/ServerTest.php | 2 +- .../Soap/Wsdl/ComplexTypeStrategyTest.php | 2 +- .../Unit/Model/Soap/Wsdl/GeneratorTest.php | 2 +- .../Test/Unit/Model/Soap/WsdlFactoryTest.php | 2 +- .../Test/Unit/Model/WebapiRoleLocatorTest.php | 2 +- .../Webapi/Test/Unit/Model/_files/acl.php | 2 +- .../Webapi/Test/Unit/Model/_files/acl.xml | 2 +- .../Webapi/Test/Unit/Model/_files/acl.xsd | 2 +- .../soap_fault/soap_fault_expected_xmls.php | 2 +- .../Test/Unit/_files/test_interfaces.php | 31 - app/code/Magento/Webapi/composer.json | 2 +- .../Magento/Webapi/etc/adminhtml/system.xml | 2 +- app/code/Magento/Webapi/etc/cache.xml | 2 +- app/code/Magento/Webapi/etc/di.xml | 2 +- .../Magento/Webapi/etc/frontend/routes.xml | 2 +- app/code/Magento/Webapi/etc/module.xml | 2 +- app/code/Magento/Webapi/etc/webapi.xsd | 2 +- .../Magento/Webapi/etc/webapi_rest/di.xml | 2 +- .../Magento/Webapi/etc/webapi_soap/di.xml | 2 +- app/code/Magento/Webapi/registration.php | 2 +- .../layout/adminhtml_integration_edit.xml | 2 +- ...dminhtml_integration_permissionsdialog.xml | 2 +- .../Plugin/AnonymousResourceSecurity.php | 2 +- .../Model/Plugin/CacheInvalidator.php | 2 +- app/code/Magento/WebapiSecurity/composer.json | 2 +- .../WebapiSecurity/etc/adminhtml/system.xml | 2 +- .../Magento/WebapiSecurity/etc/config.xml | 2 +- app/code/Magento/WebapiSecurity/etc/di.xml | 2 +- .../Magento/WebapiSecurity/etc/module.xml | 2 +- .../Magento/WebapiSecurity/registration.php | 2 +- .../Block/Adminhtml/Items/Price/Renderer.php | 2 +- .../Magento/Weee/Block/Element/Weee/Tax.php | 2 +- .../Weee/Block/Item/Price/Renderer.php | 2 +- .../Magento/Weee/Block/Renderer/Weee/Tax.php | 2 +- .../Magento/Weee/Block/Sales/Order/Totals.php | 2 +- app/code/Magento/Weee/Helper/Data.php | 2 +- .../Weee/Model/App/Action/ContextPlugin.php | 2 +- .../Weee/Model/Attribute/Backend/Weee/Tax.php | 2 +- app/code/Magento/Weee/Model/Config.php | 2 +- .../Weee/Model/Config/Source/Display.php | 2 +- .../Attribute/Backend/Weee/Tax.php | 2 +- .../Magento/Weee/Model/ResourceModel/Tax.php | 2 +- .../Magento/Weee/Model/Sales/Pdf/Weee.php | 2 +- app/code/Magento/Weee/Model/Tax.php | 2 +- .../Weee/Model/Total/Creditmemo/Weee.php | 34 +- .../Magento/Weee/Model/Total/Invoice/Weee.php | 30 +- .../Magento/Weee/Model/Total/Quote/Weee.php | 2 +- .../Weee/Model/Total/Quote/WeeeTax.php | 2 +- .../Magento/Weee/Model/WeeeConfigProvider.php | 2 +- .../AddWeeeTaxAttributeTypeObserver.php | 2 +- .../Weee/Observer/AfterAddressSave.php | 2 +- .../AssignBackendModelToAttributeObserver.php | 2 +- .../Weee/Observer/CustomerLoggedIn.php | 2 +- .../GetPriceConfigurationObserver.php | 2 +- .../SetWeeeRendererInFormObserver.php | 2 +- .../Observer/UpdateElementTypesObserver.php | 2 +- .../UpdateExcludedFieldListObserver.php | 2 +- .../Observer/UpdateProductOptionsObserver.php | 2 +- .../Plugin/Checkout/CustomerData/Cart.php | 2 +- app/code/Magento/Weee/Pricing/Adjustment.php | 2 +- .../Weee/Pricing/Render/Adjustment.php | 2 +- .../Weee/Pricing/Render/TaxAdjustment.php | 2 +- .../Magento/Weee/Pricing/TaxAdjustment.php | 2 +- app/code/Magento/Weee/Setup/InstallData.php | 2 +- app/code/Magento/Weee/Setup/InstallSchema.php | 2 +- app/code/Magento/Weee/Setup/Recurring.php | 2 +- .../Unit/App/Action/ContextPluginTest.php | 2 +- .../Test/Unit/Block/Element/Weee/TaxTest.php | 2 +- .../Unit/Block/Item/Price/RendererTest.php | 2 +- .../Weee/Test/Unit/Helper/DataTest.php | 2 +- .../Model/Attribute/Backend/Weee/TaxTest.php | 2 +- .../Weee/Test/Unit/Model/ConfigTest.php | 2 +- .../Attribute/Backend/Weee/TaxTest.php | 2 +- .../Test/Unit/Model/ResourceModel/TaxTest.php | 2 +- .../Magento/Weee/Test/Unit/Model/TaxTest.php | 2 +- .../Unit/Model/Total/Creditmemo/WeeeTest.php | 17 +- .../Unit/Model/Total/Invoice/WeeeTest.php | 19 +- .../Unit/Model/Total/Quote/WeeeTaxTest.php | 2 +- .../Test/Unit/Model/Total/Quote/WeeeTest.php | 2 +- .../Unit/Model/WeeeConfigProviderTest.php | 2 +- .../Unit/Observer/AfterAddressSaveTest.php | 2 +- .../Unit/Observer/CustomerLoggedInTest.php | 2 +- .../GetPriceConfigurationObserverTest.php | 2 +- .../UpdateProductOptionsObserverTest.php | 2 +- .../Weee/Test/Unit/Pricing/AdjustmentTest.php | 2 +- .../Unit/Pricing/Render/AdjustmentTest.php | 2 +- .../Unit/Pricing/Render/TaxAdjustmentTest.php | 2 +- .../Test/Unit/Pricing/TaxAdjustmentTest.php | 2 +- .../Form/Modifier/Manager/WebsiteTest.php | 2 +- .../Product/Form/Modifier/WeeeTest.php | 2 +- .../Product/Form/Modifier/Manager/Website.php | 2 +- .../Product/Form/Modifier/Weee.php | 2 +- app/code/Magento/Weee/composer.json | 2 +- app/code/Magento/Weee/etc/adminhtml/di.xml | 2 +- .../Magento/Weee/etc/adminhtml/events.xml | 2 +- .../Magento/Weee/etc/adminhtml/system.xml | 2 +- app/code/Magento/Weee/etc/config.xml | 2 +- app/code/Magento/Weee/etc/di.xml | 2 +- app/code/Magento/Weee/etc/events.xml | 2 +- app/code/Magento/Weee/etc/fieldset.xml | 2 +- app/code/Magento/Weee/etc/frontend/di.xml | 2 +- app/code/Magento/Weee/etc/frontend/events.xml | 2 +- app/code/Magento/Weee/etc/module.xml | 2 +- app/code/Magento/Weee/etc/pdf.xml | 2 +- app/code/Magento/Weee/etc/sales.xml | 2 +- app/code/Magento/Weee/registration.php | 2 +- .../adminhtml/layout/catalog_product_form.xml | 2 +- .../layout/sales_creditmemo_item_price.xml | 2 +- .../layout/sales_invoice_item_price.xml | 2 +- .../layout/sales_order_create_item_price.xml | 2 +- .../layout/sales_order_creditmemo_new.xml | 2 +- .../sales_order_creditmemo_updateqty.xml | 2 +- .../layout/sales_order_creditmemo_view.xml | 2 +- .../layout/sales_order_invoice_new.xml | 2 +- .../layout/sales_order_invoice_updateqty.xml | 2 +- .../layout/sales_order_invoice_view.xml | 2 +- .../layout/sales_order_item_price.xml | 2 +- .../adminhtml/layout/sales_order_view.xml | 2 +- .../Weee/view/adminhtml/requirejs-config.js | 4 +- .../adminhtml/templates/items/price/row.phtml | 2 +- .../templates/items/price/total.phtml | 2 +- .../templates/items/price/unit.phtml | 2 +- .../order/create/items/price/row.phtml | 2 +- .../order/create/items/price/total.phtml | 2 +- .../order/create/items/price/unit.phtml | 2 +- .../adminhtml/templates/renderer/tax.phtml | 2 +- .../product_attribute_add_form.xml | 2 +- .../view/adminhtml/web/js/fpt-attribute.js | 50 +- .../Weee/view/adminhtml/web/js/fpt-group.js | 2 +- .../adminhtml/web/js/regions-tax-select.js | 2 +- .../base/layout/catalog_product_prices.xml | 2 +- .../base/templates/pricing/adjustment.phtml | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../frontend/layout/checkout_index_index.xml | 4 +- .../layout/checkout_item_price_renderers.xml | 2 +- .../Weee/view/frontend/layout/default.xml | 2 +- .../layout/sales_email_item_price.xml | 2 +- .../sales_email_order_creditmemo_items.xml | 2 +- .../sales_email_order_invoice_items.xml | 2 +- .../layout/sales_email_order_items.xml | 2 +- .../layout/sales_guest_creditmemo.xml | 2 +- .../frontend/layout/sales_guest_invoice.xml | 2 +- .../frontend/layout/sales_guest_print.xml | 2 +- .../layout/sales_guest_printcreditmemo.xml | 2 +- .../layout/sales_guest_printinvoice.xml | 2 +- .../view/frontend/layout/sales_guest_view.xml | 2 +- .../layout/sales_order_creditmemo.xml | 2 +- .../frontend/layout/sales_order_invoice.xml | 2 +- .../layout/sales_order_item_price.xml | 2 +- .../frontend/layout/sales_order_print.xml | 2 +- .../layout/sales_order_printcreditmemo.xml | 2 +- .../layout/sales_order_printinvoice.xml | 2 +- .../view/frontend/layout/sales_order_view.xml | 2 +- .../Weee/view/frontend/requirejs-config.js | 6 +- .../checkout/cart/item/price/sidebar.phtml | 2 +- .../review/item/price/row_excl_tax.phtml | 2 +- .../review/item/price/row_incl_tax.phtml | 2 +- .../review/item/price/unit_excl_tax.phtml | 2 +- .../review/item/price/unit_incl_tax.phtml | 2 +- .../templates/email/items/price/row.phtml | 2 +- .../frontend/templates/item/price/row.phtml | 2 +- .../item/price/total_after_discount.phtml | 2 +- .../frontend/templates/item/price/unit.phtml | 2 +- .../frontend/web/js/view/cart/totals/weee.js | 32 +- .../summary/item/price/row_excl_tax.js | 100 +- .../summary/item/price/row_incl_tax.js | 102 +- .../view/checkout/summary/item/price/weee.js | 74 +- .../web/js/view/checkout/summary/weee.js | 84 +- .../Weee/view/frontend/web/tax-toggle.js | 8 +- .../summary/item/price/row_excl_tax.html | 2 +- .../summary/item/price/row_incl_tax.html | 2 +- .../web/template/checkout/summary/weee.html | 4 +- .../Magento/Widget/Block/Adminhtml/Widget.php | 2 +- .../Widget/Catalog/Category/Chooser.php | 2 +- .../Widget/Block/Adminhtml/Widget/Chooser.php | 2 +- .../Widget/Block/Adminhtml/Widget/Form.php | 2 +- .../Block/Adminhtml/Widget/Instance.php | 2 +- .../Block/Adminhtml/Widget/Instance/Edit.php | 2 +- .../Instance/Edit/Chooser/Container.php | 2 +- .../Edit/Chooser/DesignAbstraction.php | 2 +- .../Widget/Instance/Edit/Chooser/Layout.php | 2 +- .../Widget/Instance/Edit/Chooser/Template.php | 2 +- .../Adminhtml/Widget/Instance/Edit/Form.php | 2 +- .../Widget/Instance/Edit/Tab/Main.php | 2 +- .../Widget/Instance/Edit/Tab/Main/Layout.php | 2 +- .../Widget/Instance/Edit/Tab/Properties.php | 2 +- .../Widget/Instance/Edit/Tab/Settings.php | 2 +- .../Adminhtml/Widget/Instance/Edit/Tabs.php | 2 +- .../Widget/Block/Adminhtml/Widget/Options.php | 2 +- .../Magento/Widget/Block/BlockInterface.php | 2 +- .../Adminhtml/Widget/BuildWidget.php | 2 +- .../Controller/Adminhtml/Widget/Index.php | 2 +- .../Controller/Adminhtml/Widget/Instance.php | 2 +- .../Adminhtml/Widget/Instance/Blocks.php | 2 +- .../Adminhtml/Widget/Instance/Categories.php | 2 +- .../Adminhtml/Widget/Instance/Delete.php | 2 +- .../Adminhtml/Widget/Instance/Edit.php | 2 +- .../Adminhtml/Widget/Instance/Index.php | 2 +- .../Adminhtml/Widget/Instance/NewAction.php | 2 +- .../Adminhtml/Widget/Instance/Products.php | 2 +- .../Adminhtml/Widget/Instance/Save.php | 2 +- .../Adminhtml/Widget/Instance/Template.php | 2 +- .../Adminhtml/Widget/Instance/Validate.php | 2 +- .../Adminhtml/Widget/LoadOptions.php | 2 +- app/code/Magento/Widget/Helper/Conditions.php | 33 +- .../Magento/Widget/Model/Config/Converter.php | 2 +- app/code/Magento/Widget/Model/Config/Data.php | 2 +- .../Widget/Model/Config/FileResolver.php | 2 +- .../Magento/Widget/Model/Config/Reader.php | 2 +- .../Widget/Model/Config/SchemaLocator.php | 2 +- app/code/Magento/Widget/Model/Layout/Link.php | 2 +- .../Magento/Widget/Model/Layout/Update.php | 2 +- .../Widget/Model/NamespaceResolver.php | 2 +- .../Model/ResourceModel/Layout/Link.php | 2 +- .../ResourceModel/Layout/Link/Collection.php | 2 +- .../Model/ResourceModel/Layout/Plugin.php | 2 +- .../Model/ResourceModel/Layout/Update.php | 2 +- .../Layout/Update/Collection.php | 2 +- .../Widget/Model/ResourceModel/Widget.php | 9 +- .../Model/ResourceModel/Widget/Instance.php | 2 +- .../Widget/Instance/Collection.php | 2 +- .../Widget/Instance/Options/ThemeId.php | 12 +- .../Widget/Instance/Options/Themes.php | 41 + .../Widget/Instance/Options/Types.php | 2 +- .../Magento/Widget/Model/Template/Filter.php | 2 +- .../Widget/Model/Template/FilterEmulate.php | 2 +- app/code/Magento/Widget/Model/Widget.php | 2 +- .../Magento/Widget/Model/Widget/Config.php | 2 +- .../Magento/Widget/Model/Widget/Instance.php | 20 +- .../Model/Widget/Instance/OptionsFactory.php | 2 +- app/code/Magento/Widget/Setup/InstallData.php | 2 +- .../Magento/Widget/Setup/InstallSchema.php | 2 +- app/code/Magento/Widget/Setup/UpgradeData.php | 53 + .../Widget/Catalog/Category/ChooserTest.php | 2 +- .../Edit/Chooser/AbstractContainerTest.php | 2 +- .../Instance/Edit/Chooser/ContainerTest.php | 14 +- .../Instance/Edit/Tab/PropertiesTest.php | 2 +- .../Widget/Instance/CategoriesTest.php | 2 +- .../Adminhtml/Widget/LoadOptionsTest.php | 2 +- .../Test/Unit/Helper/ConditionsTest.php | 18 +- .../Test/Unit/Model/Config/ConverterTest.php | 2 +- .../Unit/Model/Config/FileResolverTest.php | 2 +- .../Test/Unit/Model/NamespaceResolverTest.php | 2 +- .../ResourceModel/Layout/AbstractTestCase.php | 2 +- .../Layout/Link/CollectionTest.php | 2 +- .../Layout/Update/CollectionTest.php | 2 +- .../Widget/Instance/Options/ThemesTest.php | 55 + .../Unit/Model/Template/FilterEmulateTest.php | 2 +- .../Test/Unit/Model/Template/FilterTest.php | 2 +- .../Test/Unit/Model/Widget/InstanceTest.php | 2 +- .../Widget/Test/Unit/Model/WidgetTest.php | 2 +- .../Unit/Model/_files/mappedConfigArray1.php | 2 +- .../Unit/Model/_files/mappedConfigArray2.php | 2 +- .../Model/_files/mappedConfigArrayAll.php | 2 +- .../Widget/Test/Unit/Model/_files/widget.xml | 2 +- .../Test/Unit/Model/_files/widget_config.php | 2 +- app/code/Magento/Widget/composer.json | 2 +- app/code/Magento/Widget/etc/acl.xml | 2 +- app/code/Magento/Widget/etc/adminhtml/di.xml | 2 +- .../Magento/Widget/etc/adminhtml/menu.xml | 2 +- .../Magento/Widget/etc/adminhtml/routes.xml | 2 +- app/code/Magento/Widget/etc/di.xml | 2 +- app/code/Magento/Widget/etc/module.xml | 4 +- app/code/Magento/Widget/etc/types.xsd | 4 +- app/code/Magento/Widget/etc/widget.xsd | 2 +- app/code/Magento/Widget/etc/widget_file.xsd | 2 +- app/code/Magento/Widget/registration.php | 2 +- .../layout/adminhtml_widget_index.xml | 2 +- .../adminhtml_widget_instance_block.xml | 4 +- .../layout/adminhtml_widget_instance_edit.xml | 2 +- .../adminhtml_widget_instance_index.xml | 2 +- .../layout/adminhtml_widget_loadoptions.xml | 2 +- .../catalog/category/widget/tree.phtml | 4 +- .../templates/instance/edit/layout.phtml | 4 +- .../adminhtml/templates/instance/js.phtml | 2 +- .../Widget/view/frontend/layout/default.xml | 2 +- .../Widget/view/frontend/layout/print.xml | 2 +- .../Magento/Wishlist/Block/AbstractBlock.php | 2 +- .../Magento/Wishlist/Block/AddToWishlist.php | 2 +- .../Widget/Grid/Column/Filter/Text.php | 2 +- .../Wishlist/Block/Adminhtml/WishlistTab.php | 2 +- .../Item/Renderer/Actions/MoveToWishlist.php | 2 +- .../ProductList/Item/AddTo/Wishlist.php | 2 +- .../Catalog/Product/View/AddTo/Wishlist.php | 2 +- .../Wishlist/Block/Customer/Sharing.php | 2 +- .../Wishlist/Block/Customer/Sidebar.php | 2 +- .../Wishlist/Block/Customer/Wishlist.php | 2 +- .../Block/Customer/Wishlist/Button.php | 2 +- .../Block/Customer/Wishlist/Item/Column.php | 2 +- .../Customer/Wishlist/Item/Column/Actions.php | 2 +- .../Customer/Wishlist/Item/Column/Cart.php | 2 +- .../Customer/Wishlist/Item/Column/Comment.php | 2 +- .../Customer/Wishlist/Item/Column/Edit.php | 2 +- .../Customer/Wishlist/Item/Column/Image.php | 2 +- .../Customer/Wishlist/Item/Column/Info.php | 2 +- .../Customer/Wishlist/Item/Column/Remove.php | 2 +- .../Block/Customer/Wishlist/Item/Options.php | 2 +- .../Block/Customer/Wishlist/Items.php | 2 +- .../Magento/Wishlist/Block/Item/Configure.php | 2 +- app/code/Magento/Wishlist/Block/Link.php | 14 +- .../Magento/Wishlist/Block/Rss/EmailLink.php | 2 +- app/code/Magento/Wishlist/Block/Rss/Link.php | 2 +- .../Wishlist/Block/Share/Email/Items.php | 2 +- .../Magento/Wishlist/Block/Share/Wishlist.php | 2 +- .../Wishlist/Controller/AbstractIndex.php | 2 +- .../Magento/Wishlist/Controller/Index/Add.php | 2 +- .../Wishlist/Controller/Index/Allcart.php | 2 +- .../Wishlist/Controller/Index/Cart.php | 7 +- .../Wishlist/Controller/Index/Configure.php | 2 +- .../Controller/Index/DownloadCustomOption.php | 2 +- .../Wishlist/Controller/Index/Fromcart.php | 2 +- .../Wishlist/Controller/Index/Index.php | 2 +- .../Wishlist/Controller/Index/Plugin.php | 2 +- .../Wishlist/Controller/Index/Remove.php | 2 +- .../Wishlist/Controller/Index/Send.php | 2 +- .../Wishlist/Controller/Index/Share.php | 2 +- .../Wishlist/Controller/Index/Update.php | 2 +- .../Controller/Index/UpdateItemOptions.php | 2 +- .../Wishlist/Controller/IndexInterface.php | 2 +- .../Wishlist/Controller/Shared/Allcart.php | 2 +- .../Wishlist/Controller/Shared/Cart.php | 2 +- .../Wishlist/Controller/Shared/Index.php | 2 +- .../Controller/Shared/WishlistProvider.php | 2 +- .../Wishlist/Controller/WishlistProvider.php | 2 +- .../Controller/WishlistProviderInterface.php | 2 +- .../Wishlist/CustomerData/Wishlist.php | 2 +- app/code/Magento/Wishlist/Helper/Data.php | 2 +- app/code/Magento/Wishlist/Helper/Rss.php | 2 +- .../Wishlist/Model/AuthenticationState.php | 2 +- .../Model/AuthenticationStateInterface.php | 2 +- app/code/Magento/Wishlist/Model/Config.php | 2 +- .../Wishlist/Model/Config/Source/Summary.php | 2 +- app/code/Magento/Wishlist/Model/Item.php | 21 +- .../Magento/Wishlist/Model/Item/Option.php | 2 +- .../Magento/Wishlist/Model/ItemCarrier.php | 2 +- .../Model/LocaleQuantityProcessor.php | 2 +- .../Wishlist/Model/ResourceModel/Item.php | 2 +- .../Model/ResourceModel/Item/Collection.php | 14 +- .../ResourceModel/Item/Collection/Grid.php | 2 +- .../Model/ResourceModel/Item/Option.php | 2 +- .../ResourceModel/Item/Option/Collection.php | 2 +- .../Wishlist/Model/ResourceModel/Wishlist.php | 2 +- .../ResourceModel/Wishlist/Collection.php | 2 +- .../Magento/Wishlist/Model/Rss/Wishlist.php | 2 +- app/code/Magento/Wishlist/Model/Wishlist.php | 2 +- .../Magento/Wishlist/Observer/AddToCart.php | 2 +- .../Wishlist/Observer/CartUpdateBefore.php | 2 +- .../Wishlist/Observer/CustomerLogin.php | 2 +- .../Wishlist/Observer/CustomerLogout.php | 2 +- .../ConfiguredPrice/ConfigurableProduct.php | 13 +- .../Pricing/ConfiguredPrice/Downloadable.php | 2 +- .../Pricing/Render/ConfiguredPriceBox.php | 2 +- .../Magento/Wishlist/Setup/InstallSchema.php | 2 +- app/code/Magento/Wishlist/Setup/Recurring.php | 2 +- .../Magento/Wishlist/Setup/UpgradeData.php | 126 + .../Widget/Grid/Column/Filter/TextTest.php | 2 +- .../Renderer/Actions/MoveToWishlistTest.php | 2 +- .../Test/Unit/Block/Customer/SidebarTest.php | 2 +- .../Customer/Wishlist/Item/OptionsTest.php | 2 +- .../Test/Unit/Block/Item/ConfigureTest.php | 2 +- .../Test/Unit/Block/Rss/EmailLinkTest.php | 2 +- .../Wishlist/Test/Unit/Block/Rss/LinkTest.php | 2 +- .../Unit/Controller/Index/AllcartTest.php | 2 +- .../Test/Unit/Controller/Index/CartTest.php | 176 +- .../Unit/Controller/Index/FromcartTest.php | 2 +- .../Test/Unit/Controller/Index/IndexTest.php | 2 +- .../Test/Unit/Controller/Index/PluginTest.php | 2 +- .../Test/Unit/Controller/Index/RemoveTest.php | 2 +- .../Test/Unit/Controller/Index/SendTest.php | 2 +- .../Test/Unit/Controller/Index/ShareTest.php | 2 +- .../Index/UpdateItemOptionsTest.php | 2 +- .../Unit/Controller/Shared/AllcartTest.php | 2 +- .../Test/Unit/Controller/Shared/CartTest.php | 2 +- .../Unit/Controller/WishlistProviderTest.php | 2 +- .../Test/Unit/CustomerData/WishlistTest.php | 2 +- .../Wishlist/Test/Unit/Helper/DataTest.php | 2 +- .../Wishlist/Test/Unit/Helper/RssTest.php | 2 +- .../Unit/Model/AuthenticationStateTest.php | 2 +- .../Wishlist/Test/Unit/Model/ConfigTest.php | 2 +- .../Test/Unit/Model/ItemCarrierTest.php | 2 +- .../Wishlist/Test/Unit/Model/ItemTest.php | 2 +- .../Model/LocaleQuantityProcessorTest.php | 2 +- .../ResourceModel/Item/CollectionTest.php | 2 +- .../Test/Unit/Model/Rss/WishlistTest.php | 22 +- .../Wishlist/Test/Unit/Model/WishlistTest.php | 2 +- .../Test/Unit/Observer/AddToCartTest.php | 2 +- .../Unit/Observer/CartUpdateBeforeTest.php | 2 +- .../Test/Unit/Observer/CustomerLoginTest.php | 2 +- .../Test/Unit/Observer/CustomerLogoutTest.php | 2 +- .../ConfigurableProductTest.php | 44 +- .../ConfiguredPrice/DownloadableTest.php | 2 +- .../Pricing/Render/ConfiguredPriceBoxTest.php | 2 +- app/code/Magento/Wishlist/composer.json | 2 +- app/code/Magento/Wishlist/etc/acl.xml | 2 +- .../Magento/Wishlist/etc/adminhtml/di.xml | 2 +- .../Magento/Wishlist/etc/adminhtml/system.xml | 2 +- .../Wishlist/etc/catalog_attributes.xml | 8 +- app/code/Magento/Wishlist/etc/config.xml | 2 +- app/code/Magento/Wishlist/etc/di.xml | 2 +- .../Magento/Wishlist/etc/email_templates.xml | 2 +- app/code/Magento/Wishlist/etc/events.xml | 2 +- app/code/Magento/Wishlist/etc/frontend/di.xml | 2 +- .../Magento/Wishlist/etc/frontend/events.xml | 2 +- .../Wishlist/etc/frontend/page_types.xml | 2 +- .../Magento/Wishlist/etc/frontend/routes.xml | 4 +- .../Wishlist/etc/frontend/sections.xml | 2 +- app/code/Magento/Wishlist/etc/module.xml | 4 +- app/code/Magento/Wishlist/etc/view.xml | 2 +- app/code/Magento/Wishlist/registration.php | 2 +- .../adminhtml/layout/customer_index_edit.xml | 2 +- .../layout/customer_index_wishlist.xml | 2 +- .../customer/edit/tab/wishlist.phtml | 2 +- .../base/layout/catalog_product_prices.xml | 2 +- .../frontend/email/share_notification.html | 2 +- .../frontend/layout/catalog_category_view.xml | 2 +- .../frontend/layout/catalog_product_view.xml | 4 +- .../layout/catalogsearch_advanced_result.xml | 2 +- .../layout/catalogsearch_result_index.xml | 2 +- .../frontend/layout/checkout_cart_index.xml | 2 +- .../layout/checkout_cart_item_renderers.xml | 2 +- .../view/frontend/layout/customer_account.xml | 5 +- .../Wishlist/view/frontend/layout/default.xml | 8 +- .../frontend/layout/wishlist_email_items.xml | 2 +- .../frontend/layout/wishlist_email_rss.xml | 2 +- .../layout/wishlist_index_configure.xml | 4 +- .../wishlist_index_configure_type_bundle.xml | 6 +- ...list_index_configure_type_configurable.xml | 2 +- ...list_index_configure_type_downloadable.xml | 2 +- .../wishlist_index_configure_type_grouped.xml | 2 +- .../wishlist_index_configure_type_simple.xml | 2 +- .../frontend/layout/wishlist_index_index.xml | 2 +- .../frontend/layout/wishlist_index_share.xml | 2 +- .../frontend/layout/wishlist_shared_index.xml | 2 +- .../view/frontend/requirejs-config.js | 6 +- .../view/frontend/templates/addto.phtml | 2 +- .../frontend/templates/button/share.phtml | 2 +- .../frontend/templates/button/tocart.phtml | 2 +- .../frontend/templates/button/update.phtml | 2 +- .../renderer/actions/move_to_wishlist.phtml | 2 +- .../catalog/product/list/addto/wishlist.phtml | 2 +- .../catalog/product/view/addto/wishlist.phtml | 2 +- .../view/frontend/templates/email/items.phtml | 2 +- .../templates/item/column/actions.phtml | 2 +- .../frontend/templates/item/column/cart.phtml | 2 +- .../templates/item/column/comment.phtml | 2 +- .../frontend/templates/item/column/edit.phtml | 2 +- .../templates/item/column/image.phtml | 2 +- .../frontend/templates/item/column/name.phtml | 2 +- .../templates/item/column/price.phtml | 2 +- .../templates/item/column/remove.phtml | 2 +- .../templates/item/configure/addto.phtml | 4 +- .../item/configure/addto/wishlist.phtml | 4 +- .../view/frontend/templates/item/list.phtml | 2 +- .../frontend/templates/js/components.phtml | 2 +- .../view/frontend/templates/link.phtml | 2 +- .../messages/addProductSuccessMessage.phtml | 2 +- .../frontend/templates/options_list.phtml | 2 +- .../view/frontend/templates/rss/email.phtml | 2 +- .../frontend/templates/rss/wishlist.phtml | 2 +- .../view/frontend/templates/shared.phtml | 2 +- .../view/frontend/templates/sharing.phtml | 2 +- .../view/frontend/templates/sidebar.phtml | 2 +- .../view/frontend/templates/view.phtml | 2 +- .../view/frontend/web/js/add-to-wishlist.js | 163 +- .../Wishlist/view/frontend/web/js/search.js | 19 +- .../view/frontend/web/js/view/wishlist.js | 4 +- .../view/frontend/web/{ => js}/wishlist.js | 61 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../Magento_Backend/layout/default.xml | 2 +- .../backend/Magento_Backend/layout/styles.xml | 2 +- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/module/_footer.less | 2 +- .../web/css/source/module/_header.less | 2 +- .../web/css/source/module/_main.less | 2 +- .../web/css/source/module/_menu.less | 2 +- .../source/module/header/_actions-group.less | 2 +- .../source/module/header/_headings-group.less | 2 +- .../header/actions-group/_notifications.less | 2 +- .../module/header/actions-group/_search.less | 2 +- .../module/header/actions-group/_user.less | 2 +- .../header/headings-group/_breadcrumbs.less | 2 +- .../css/source/module/main/_actions-bar.less | 15 +- .../module/main/_collapsible-blocks.less | 2 +- .../web/css/source/module/main/_page-nav.less | 2 +- .../css/source/module/main/_store-scope.less | 2 +- .../main/actions-bar/_store-switcher.less | 2 +- .../module/pages/_cache-management.less | 2 +- .../css/source/module/pages/_dashboard.less | 2 +- .../web/css/source/module/pages/_login.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module.less | 18 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module.less | 2 +- .../_attributes_template_popup.less | 2 +- .../module/components/_currency-addon.less | 2 +- .../css/source/module/components/_grid.less | 2 +- .../module/components/_navigation-bar.less | 2 +- .../module/components/_steps-wizard.less | 2 +- .../components/navigation-bar/_buttons.less | 2 +- .../navigation-bar/_navigation-bar.less | 2 +- .../module/steps/_attribute-values.less | 2 +- .../css/source/module/steps/_bulk-images.less | 2 +- .../module/steps/_select-attributes.less | 2 +- .../web/css/source/module/steps/_summary.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module.less | 2 +- .../Magento_Enterprise/layout/default.xml | 2 +- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../Magento_Rma/web/css/source/_module.less | 2 +- .../Magento_Sales/web/css/source/_module.less | 2 +- .../web/css/source/module/_edit-order.less | 2 +- .../web/css/source/module/_order.less | 2 +- .../web/css/source/module/order/_address.less | 2 +- .../css/source/module/order/_discounts.less | 2 +- .../source/module/order/_gift-options.less | 2 +- .../web/css/source/module/order/_items.less | 2 +- .../source/module/order/_order-account.less | 2 +- .../source/module/order/_order-comments.less | 2 +- .../module/order/_payment-shipping.less | 2 +- .../web/css/source/module/order/_sidebar.less | 2 +- .../web/css/source/module/order/_sku.less | 2 +- .../web/css/source/module/order/_total.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 13 + .../web/css/source/_module.less | 2 +- .../module/_scheduled-changes-modal.less | 2 +- .../css/source/module/_scheduled-changes.less | 2 +- .../source/module/_staging-data-tooltip.less | 2 +- .../web/css/source/_module-old.less | 2 +- .../Magento_Tax/web/css/source/_module.less | 2 +- .../web/css/source/_module-old.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module-old.less | 2 +- .../Magento_Ui/web/css/source/_module.less | 2 +- .../web/css/source/module/_data-grid.less | 2 +- .../module/data-grid/_data-grid-header.less | 2 +- .../module/data-grid/_data-grid-static.less | 2 +- .../_data-grid-action-bookmarks.less | 2 +- .../_data-grid-action-columns.less | 2 +- .../_data-grid-action-export.less | 2 +- .../data-grid-header/_data-grid-filters.less | 2 +- .../data-grid-header/_data-grid-pager.less | 2 +- .../_data-grid-sticky-header.less | 2 +- .../Magento_Vault/web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../adminhtml/Magento/backend/composer.json | 2 +- .../adminhtml/Magento/backend/etc/view.xml | 2 +- .../Magento/backend/registration.php | 2 +- .../adminhtml/Magento/backend/theme.xml | 2 +- .../web/app/setup/styles/less/_setup.less | 2 +- .../styles/less/components/_messages.less | 2 +- .../less/components/_navigation-bar.less | 2 +- .../less/components/_progress-bars.less | 2 +- .../styles/less/components/_tooltips.less | 2 +- .../tooltips/_password-strength.less | 2 +- .../less/components/tooltips/_tooltips.less | 2 +- .../app/setup/styles/less/lib/_buttons.less | 2 +- .../app/setup/styles/less/lib/_classes.less | 2 +- .../app/setup/styles/less/lib/_collector.less | 2 +- .../app/setup/styles/less/lib/_extends.less | 2 +- .../web/app/setup/styles/less/lib/_forms.less | 2 +- .../web/app/setup/styles/less/lib/_icons.less | 2 +- .../web/app/setup/styles/less/lib/_lists.less | 2 +- .../setup/styles/less/lib/_structures.less | 2 +- .../app/setup/styles/less/lib/_utilities.less | 2 +- .../app/setup/styles/less/lib/_variables.less | 2 +- .../less/lib/forms/_checkbox-radio.less | 2 +- .../setup/styles/less/lib/forms/_forms.less | 2 +- .../setup/styles/less/lib/forms/_legends.less | 2 +- .../styles/less/lib/forms/_multiselects.less | 2 +- .../setup/styles/less/lib/forms/_selects.less | 2 +- .../styles/less/lib/forms/_validation.less | 2 +- .../less/lib/utilities/_animations.less | 2 +- .../less/lib/utilities/_grid-framework.less | 2 +- .../styles/less/lib/utilities/_grid.less | 2 +- .../less/lib/utilities/_vendor-prefixes.less | 2 +- .../app/setup/styles/less/pages/_common.less | 2 +- .../less/pages/_customize-your-store.less | 2 +- .../app/setup/styles/less/pages/_install.less | 2 +- .../app/setup/styles/less/pages/_landing.less | 2 +- .../app/setup/styles/less/pages/_license.less | 2 +- .../styles/less/pages/_readiness-check.less | 2 +- .../styles/less/pages/_web-configuration.less | 2 +- .../styles/less/components/_data-grid.less | 2 +- .../styles/less/components/_header.less | 2 +- .../updater/styles/less/components/_menu.less | 2 +- .../styles/less/components/_modals.less | 2 +- .../components/_navigation-bar_extend.less | 2 +- .../styles/less/components/_page-inner.less | 2 +- .../updater/styles/less/pages/_common.less | 2 +- .../styles/less/pages/_extension-manager.less | 2 +- .../app/updater/styles/less/pages/_home.less | 2 +- .../app/updater/styles/less/pages/_login.less | 2 +- .../updater/styles/less/source/_extends.less | 2 +- .../updater/styles/less/source/_forms.less | 2 +- .../updater/styles/less/source/_lists.less | 2 +- .../styles/less/source/_structure.less | 2 +- .../styles/less/source/_typography.less | 2 +- .../styles/less/source/_variables.less | 2 +- .../backend/web/css/source/_actions.less | 2 +- .../backend/web/css/source/_classes.less | 2 +- .../backend/web/css/source/_components.less | 2 +- .../backend/web/css/source/_extends.less | 2 +- .../backend/web/css/source/_forms.less | 2 +- .../Magento/backend/web/css/source/_grid.less | 2 +- .../backend/web/css/source/_icons.less | 2 +- .../backend/web/css/source/_lists.less | 2 +- .../backend/web/css/source/_reset.less | 2 +- .../backend/web/css/source/_responsive.less | 2 +- .../backend/web/css/source/_sources.less | 2 +- .../backend/web/css/source/_structure.less | 2 +- .../backend/web/css/source/_tables.less | 2 +- .../Magento/backend/web/css/source/_tabs.less | 2 +- .../backend/web/css/source/_theme.less | 2 +- .../backend/web/css/source/_typography.less | 2 +- .../backend/web/css/source/_utilities.less | 2 +- .../backend/web/css/source/_variables.less | 2 +- .../css/source/actions/_actions-dropdown.less | 2 +- .../source/actions/_actions-multicheck.less | 2 +- .../source/actions/_actions-multiselect.less | 2 +- .../css/source/actions/_actions-select.less | 2 +- .../css/source/actions/_actions-split.less | 2 +- .../css/source/actions/_actions-switcher.less | 2 +- .../css/source/components/_calendar-temp.less | 2 +- .../css/source/components/_data-tooltip.less | 2 +- .../source/components/_file-insertion.less | 2 +- .../css/source/components/_file-uploader.less | 165 +- .../css/source/components/_media-gallery.less | 2 +- .../web/css/source/components/_messages.less | 4 +- .../css/source/components/_modals_extend.less | 4 +- .../css/source/components/_popups-old.less | 2 +- .../web/css/source/components/_popups.less | 2 +- .../source/components/_resizable-block.less | 2 +- .../css/source/components/_rules-temp.less | 2 +- .../web/css/source/components/_slider.less | 2 +- .../web/css/source/components/_spinner.less | 2 +- .../web/css/source/components/_timeline.less | 2 +- .../web/css/source/forms/_controls.less | 2 +- .../web/css/source/forms/_extends.less | 2 +- .../backend/web/css/source/forms/_fields.less | 2 +- .../web/css/source/forms/_form-wysiwyg.less | 2 +- .../backend/web/css/source/forms/_temp.less | 2 +- .../forms/controls/_checkbox-radio.less | 2 +- .../forms/fields/_control-collapsible.less | 2 +- .../source/forms/fields/_control-table.less | 3 +- .../css/source/forms/fields/_field-reset.less | 2 +- .../source/forms/fields/_field-tooltips.less | 2 +- .../web/css/source/utilities/_actions.less | 2 +- .../web/css/source/utilities/_animations.less | 2 +- .../css/source/utilities/_grid-framework.less | 2 +- .../web/css/source/utilities/_grid.less | 2 +- .../web/css/source/utilities/_spinner.less | 2 +- .../web/css/source/variables/_actions.less | 2 +- .../web/css/source/variables/_animations.less | 2 +- .../web/css/source/variables/_colors.less | 2 +- .../web/css/source/variables/_components.less | 2 +- .../web/css/source/variables/_data-grid.less | 2 +- .../web/css/source/variables/_forms.less | 2 +- .../web/css/source/variables/_icons.less | 3 +- .../web/css/source/variables/_spinner.less | 2 +- .../web/css/source/variables/_structure.less | 2 +- .../web/css/source/variables/_typography.less | 2 +- .../Magento/backend/web/css/styles-old.less | 7 +- .../Magento/backend/web/css/styles.less | 2 +- .../web/fonts/admin-icons/admin-icons.eot | Bin 11148 -> 11304 bytes .../web/fonts/admin-icons/admin-icons.svg | 2 +- .../web/fonts/admin-icons/admin-icons.ttf | Bin 10984 -> 11140 bytes .../web/fonts/admin-icons/admin-icons.woff | Bin 11060 -> 11216 bytes .../web/fonts/admin-icons/admin-icons.woff2 | Bin 5752 -> 6088 bytes .../web/fonts/admin-icons/selection.json | 4674 +++++------ .../adminhtml/Magento/backend/web/js/theme.js | 125 +- .../backend/web/mui/clearless/_all.less | 2 +- .../backend/web/mui/clearless/_arrows.less | 2 +- .../backend/web/mui/clearless/_helpers.less | 2 +- .../backend/web/mui/clearless/_icons.less | 2 +- .../backend/web/mui/clearless/_settings.less | 2 +- .../backend/web/mui/clearless/_sprites.less | 2 +- .../backend/web/mui/styles/_abstract.less | 2 +- .../Magento/backend/web/mui/styles/_base.less | 2 +- .../backend/web/mui/styles/_table.less | 2 +- .../Magento/backend/web/mui/styles/_vars.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_widgets.less | 2 +- .../web/css/source/_widgets.less | 2 +- .../web/css/source/_module.less | 2 +- .../Magento_Bundle/web/css/source/_email.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_widgets.less | 2 +- .../web/css/source/module/_listings.less | 2 +- .../web/css/source/module/_toolbar.less | 10 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_widgets.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/module/_cart.less | 66 +- .../web/css/source/module/_checkout.less | 2 +- .../web/css/source/module/_minicart.less | 2 +- .../module/checkout/_authentication.less | 2 +- .../module/checkout/_checkout-agreements.less | 2 +- .../css/source/module/checkout/_checkout.less | 21 +- .../module/checkout/_estimated-total.less | 2 +- .../css/source/module/checkout/_fields.less | 2 +- .../css/source/module/checkout/_modals.less | 2 +- .../module/checkout/_order-summary.less | 20 +- .../module/checkout/_payment-options.less | 2 +- .../css/source/module/checkout/_payments.less | 41 +- .../source/module/checkout/_progress-bar.less | 2 +- .../module/checkout/_shipping-policy.less | 2 +- .../css/source/module/checkout/_shipping.less | 2 +- .../_sidebar-shipping-information.less | 2 +- .../css/source/module/checkout/_sidebar.less | 2 +- .../css/source/module/checkout/_tooltip.less | 2 +- .../Magento_Cms/web/css/source/_widgets.less | 2 +- .../web/css/source/_module.less | 20 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_widgets.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../Magento_Msrp/web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_widgets.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/module/_billing.less | 2 +- .../web/css/source/module/_paypal-button.less | 2 +- .../web/css/source/module/_review.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_widgets.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../Magento_Rma/web/css/source/_email.less | 2 +- .../Magento_Rma/web/css/source/_module.less | 11 +- .../Magento_Sales/web/css/source/_email.less | 2 +- .../Magento_Sales/web/css/source/_module.less | 69 +- .../web/css/source/_widgets.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../layout/default_head_blocks.xml | 2 +- .../Magento_Theme/web/css/source/_module.less | 2 +- .../Magento_Vault/web/css/source/_module.less | 2 +- .../web/css/source/_widgets.less | 2 +- .../Magento_Weee/web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../frontend/Magento/blank/composer.json | 2 +- .../frontend/Magento/blank/etc/view.xml | 12 +- .../frontend/Magento/blank/registration.php | 2 +- app/design/frontend/Magento/blank/theme.xml | 2 +- .../Magento/blank/web/css/_styles.less | 2 +- .../Magento/blank/web/css/email-fonts.less | 2 +- .../Magento/blank/web/css/email-inline.less | 2 +- .../frontend/Magento/blank/web/css/email.less | 2 +- .../frontend/Magento/blank/web/css/print.less | 2 +- .../web/css/source/_actions-toolbar.less | 2 +- .../blank/web/css/source/_breadcrumbs.less | 2 +- .../blank/web/css/source/_buttons.less | 2 +- .../blank/web/css/source/_components.less | 2 +- .../blank/web/css/source/_email-base.less | 2 +- .../blank/web/css/source/_email-extend.less | 2 +- .../web/css/source/_email-variables.less | 2 +- .../blank/web/css/source/_extends.less | 2 +- .../Magento/blank/web/css/source/_forms.less | 2 +- .../Magento/blank/web/css/source/_icons.less | 2 +- .../Magento/blank/web/css/source/_layout.less | 2 +- .../blank/web/css/source/_loaders.less | 2 +- .../blank/web/css/source/_messages.less | 2 +- .../blank/web/css/source/_navigation.less | 2 +- .../Magento/blank/web/css/source/_pages.less | 2 +- .../Magento/blank/web/css/source/_popups.less | 2 +- .../Magento/blank/web/css/source/_price.less | 2 +- .../Magento/blank/web/css/source/_reset.less | 2 +- .../blank/web/css/source/_sections.less | 2 +- .../blank/web/css/source/_sources.less | 2 +- .../Magento/blank/web/css/source/_tables.less | 2 +- .../Magento/blank/web/css/source/_theme.less | 2 +- .../blank/web/css/source/_tooltips.less | 2 +- .../blank/web/css/source/_typography.less | 2 +- .../blank/web/css/source/_variables.less | 2 +- .../css/source/components/_modals_extend.less | 2 +- .../Magento/blank/web/css/styles-l.less | 2 +- .../Magento/blank/web/css/styles-m.less | 2 +- .../Magento/blank/web/js/navigation-menu.js | 334 +- .../Magento/blank/web/js/responsive.js | 47 +- .../frontend/Magento/blank/web/js/theme.js | 7 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_widgets.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../layout/catalog_product_view.xml | 2 +- .../luma/Magento_Catalog/layout/default.xml | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/module/_listings.less | 2 +- .../web/css/source/module/_toolbar.less | 2 +- .../web/css/source/_module.less | 2 +- .../layout/checkout_cart_index.xml | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/module/_cart.less | 59 +- .../web/css/source/module/_minicart.less | 2 +- .../css/source/module/checkout/_checkout.less | 21 +- .../module/checkout/_estimated-total.less | 2 +- .../css/source/module/checkout/_fields.less | 2 +- .../css/source/module/checkout/_modals.less | 2 +- .../module/checkout/_order-summary.less | 19 +- .../module/checkout/_payment-options.less | 2 +- .../css/source/module/checkout/_payments.less | 40 +- .../source/module/checkout/_progress-bar.less | 2 +- .../css/source/module/checkout/_shipping.less | 2 +- .../Magento_Customer/email/account_new.html | 2 +- .../layout/customer_account.xml | 29 +- .../luma/Magento_Customer/layout/default.xml | 8 +- .../web/css/source/_email.less | 2 +- .../web/css/source/_module.less | 21 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../luma/Magento_Email/email/footer.html | 2 +- .../web/css/source/_module.less | 2 +- .../layout/checkout_cart_index.xml | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../templates/layer/state.phtml | 2 +- .../templates/layer/view.phtml | 2 +- .../web/css/source/_module.less | 2 +- .../Magento_Msrp/web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../web/css/source/module/_billing.less | 2 +- .../web/css/source/module/_paypal-button.less | 2 +- .../web/css/source/module/_review.less | 2 +- .../web/css/source/_module.less | 28 +- .../web/css/source/_module.less | 2 +- .../Magento_Rma/web/css/source/_module.less | 11 +- .../Magento_Sales/email/creditmemo_new.html | 2 +- .../email/creditmemo_new_guest.html | 4 +- .../email/creditmemo_update.html | 2 +- .../email/creditmemo_update_guest.html | 2 +- .../luma/Magento_Sales/email/invoice_new.html | 2 +- .../email/invoice_new_guest.html | 2 +- .../Magento_Sales/email/invoice_update.html | 2 +- .../email/invoice_update_guest.html | 2 +- .../luma/Magento_Sales/email/order_new.html | 2 +- .../Magento_Sales/email/order_new_guest.html | 2 +- .../Magento_Sales/email/order_update.html | 2 +- .../email/order_update_guest.html | 2 +- .../Magento_Sales/email/shipment_new.html | 2 +- .../email/shipment_new_guest.html | 2 +- .../Magento_Sales/email/shipment_update.html | 2 +- .../email/shipment_update_guest.html | 2 +- .../Magento_Sales/web/css/source/_email.less | 2 +- .../Magento_Sales/web/css/source/_module.less | 62 +- .../web/css/source/_module.less | 2 +- .../luma/Magento_Theme/layout/default.xml | 2 +- .../layout/default_head_blocks.xml | 2 +- .../Magento_Theme/web/css/source/_module.less | 2 +- .../module/_collapsible_navigation.less | 9 +- .../Magento_Vault/web/css/source/_module.less | 2 +- .../web/css/source/_module.less | 2 +- .../frontend/Magento/luma/composer.json | 2 +- app/design/frontend/Magento/luma/etc/view.xml | 15 +- .../frontend/Magento/luma/registration.php | 2 +- app/design/frontend/Magento/luma/theme.xml | 2 +- .../luma/web/css/source/_actions-toolbar.less | 2 +- .../luma/web/css/source/_breadcrumbs.less | 2 +- .../Magento/luma/web/css/source/_buttons.less | 2 +- .../luma/web/css/source/_email-extend.less | 2 +- .../luma/web/css/source/_email-variables.less | 2 +- .../Magento/luma/web/css/source/_extends.less | 2 +- .../Magento/luma/web/css/source/_forms.less | 2 +- .../Magento/luma/web/css/source/_pages.less | 2 +- .../Magento/luma/web/css/source/_popups.less | 2 +- .../luma/web/css/source/_sections.less | 2 +- .../Magento/luma/web/css/source/_tables.less | 2 +- .../Magento/luma/web/css/source/_theme.less | 2 +- .../luma/web/css/source/_variables.less | 2 +- .../css/source/components/_modals_extend.less | 2 +- app/etc/NonComposerComponentRegistration.php | 2 +- app/etc/di.xml | 40 +- app/functions.php | 2 +- app/i18n/Magento/de_DE/language.xml | 2 +- app/i18n/Magento/de_DE/registration.php | 2 +- app/i18n/Magento/en_US/language.xml | 2 +- app/i18n/Magento/en_US/registration.php | 2 +- app/i18n/Magento/es_ES/language.xml | 2 +- app/i18n/Magento/es_ES/registration.php | 2 +- app/i18n/Magento/fr_FR/language.xml | 2 +- app/i18n/Magento/fr_FR/registration.php | 2 +- app/i18n/Magento/nl_NL/language.xml | 2 +- app/i18n/Magento/nl_NL/registration.php | 2 +- app/i18n/Magento/pt_BR/language.xml | 2 +- app/i18n/Magento/pt_BR/registration.php | 2 +- app/i18n/Magento/zh_Hans_CN/language.xml | 2 +- app/i18n/Magento/zh_Hans_CN/registration.php | 2 +- bin/magento | 4 +- composer.json | 35 +- composer.lock | 696 +- .../TestModule1/Controller/CookieTester.php | 2 +- .../Controller/CookieTester/DeleteCookie.php | 2 +- .../CookieTester/SetPublicCookie.php | 2 +- .../CookieTester/SetSensitiveCookie.php | 2 +- .../TestModule1/Service/V1/AllSoapAndRest.php | 2 +- .../Service/V1/AllSoapAndRestInterface.php | 2 +- .../TestModule1/Service/V1/Entity/Item.php | 2 +- .../TestModule1/Service/V2/AllSoapAndRest.php | 2 +- .../Service/V2/AllSoapAndRestInterface.php | 2 +- .../TestModule1/Service/V2/Entity/Item.php | 2 +- .../_files/Magento/TestModule1/etc/acl.xml | 2 +- .../_files/Magento/TestModule1/etc/di.xml | 2 +- .../TestModule1/etc/extension_attributes.xml | 2 +- .../TestModule1/etc/frontend/routes.xml | 4 +- .../_files/Magento/TestModule1/etc/module.xml | 2 +- .../_files/Magento/TestModule1/etc/webapi.xml | 2 +- .../Magento/TestModule1/registration.php | 2 +- .../TestModule2/Service/V1/Entity/Item.php | 2 +- .../TestModule2/Service/V1/NoWebApiXml.php | 2 +- .../Service/V1/NoWebApiXmlInterface.php | 2 +- .../TestModule2/Service/V1/SubsetRest.php | 2 +- .../Service/V1/SubsetRestInterface.php | 2 +- .../_files/Magento/TestModule2/etc/acl.xml | 2 +- .../_files/Magento/TestModule2/etc/di.xml | 2 +- .../_files/Magento/TestModule2/etc/module.xml | 2 +- .../etc/schema/AllSoapNoRestV1.xsd | 2 +- .../TestModule2/etc/schema/NoWebApiXmlV1.xsd | 2 +- .../TestModule2/etc/schema/SubsetRestV1.xsd | 2 +- .../_files/Magento/TestModule2/etc/webapi.xml | 2 +- .../Magento/TestModule2/registration.php | 2 +- .../Service/V1/Entity/Parameter.php | 2 +- .../V1/Entity/WrappedErrorParameter.php | 2 +- .../Magento/TestModule3/Service/V1/Error.php | 2 +- .../TestModule3/Service/V1/ErrorInterface.php | 2 +- .../_files/Magento/TestModule3/etc/acl.xml | 2 +- .../_files/Magento/TestModule3/etc/di.xml | 2 +- .../_files/Magento/TestModule3/etc/module.xml | 2 +- .../_files/Magento/TestModule3/etc/webapi.xml | 2 +- .../Magento/TestModule3/registration.php | 2 +- .../TestModule4/Model/ResourceModel/Item.php | 2 +- .../Service/V1/DataObjectService.php | 2 +- .../Service/V1/DataObjectServiceInterface.php | 2 +- .../Service/V1/Entity/DataObjectRequest.php | 2 +- .../Service/V1/Entity/DataObjectResponse.php | 2 +- .../Service/V1/Entity/ExtensibleRequest.php | 2 +- .../V1/Entity/ExtensibleRequestInterface.php | 2 +- .../V1/Entity/NestedDataObjectRequest.php | 2 +- .../_files/Magento/TestModule4/etc/acl.xml | 2 +- .../_files/Magento/TestModule4/etc/di.xml | 2 +- .../_files/Magento/TestModule4/etc/module.xml | 2 +- .../_files/Magento/TestModule4/etc/webapi.xml | 2 +- .../Magento/TestModule4/registration.php | 2 +- .../TestModule5/Service/V1/AllSoapAndRest.php | 2 +- .../Service/V1/AllSoapAndRestInterface.php | 2 +- .../Service/V1/Entity/AllSoapAndRest.php | 2 +- .../Service/V1/OverrideService.php | 2 +- .../Service/V1/OverrideServiceInterface.php | 2 +- .../TestModule5/Service/V2/AllSoapAndRest.php | 2 +- .../Service/V2/AllSoapAndRestInterface.php | 2 +- .../Service/V2/Entity/AllSoapAndRest.php | 2 +- .../_files/Magento/TestModule5/etc/acl.xml | 2 +- .../_files/Magento/TestModule5/etc/di.xml | 2 +- .../_files/Magento/TestModule5/etc/module.xml | 2 +- .../_files/Magento/TestModule5/etc/webapi.xml | 2 +- .../Magento/TestModule5/registration.php | 2 +- .../Api/CustomerPersistenceInterface.php | 2 +- .../Api/Data/ExtensionAttributeInterface.php | 2 +- .../Model/Address/Mapper.php | 2 +- .../Model/CustomerPersistence.php | 2 +- .../Model/Data/ExtensionAttribute.php | 2 +- .../ResourceModel/Address/ReadHandler.php | 2 +- .../ResourceModel/Address/SaveHandler.php | 2 +- .../Model/ResourceModel/ReadHandler.php | 2 +- .../Model/ResourceModel/SaveHandler.php | 2 +- .../Setup/InstallSchema.php | 2 +- .../TestModuleDefaultHydrator/etc/di.xml | 2 +- .../etc/extension_attributes.xml | 2 +- .../TestModuleDefaultHydrator/etc/webapi.xml | 4 +- .../registration.php | 2 +- .../composer.json | 4 +- .../etc/integration.xml | 2 +- .../etc/module.xml | 2 +- .../registration.php | 2 +- .../Api/TestRepositoryInterface.php | 2 +- .../Model/TestRepository.php | 2 +- .../TestModuleJoinDirectives/composer.json | 2 +- .../TestModuleJoinDirectives/etc/acl.xml | 2 +- .../TestModuleJoinDirectives/etc/di.xml | 2 +- .../etc/extension_attributes.xml | 2 +- .../TestModuleJoinDirectives/etc/module.xml | 2 +- .../TestModuleJoinDirectives/etc/webapi.xml | 2 +- .../TestModuleJoinDirectives/registration.php | 2 +- .../Api/AllSoapAndRestInterface.php | 2 +- .../CustomAttributeDataObjectInterface.php | 2 +- ...stomAttributeNestedDataObjectInterface.php | 2 +- .../TestModuleMSC/Api/Data/ItemInterface.php | 2 +- .../TestModuleMSC/Model/AllSoapAndRest.php | 2 +- .../Model/Data/CustomAttributeDataObject.php | 2 +- .../Data/CustomAttributeNestedDataObject.php | 2 +- .../Model/Data/Eav/AttributeMetadata.php | 2 +- .../Magento/TestModuleMSC/Model/Data/Item.php | 2 +- .../Model/ResourceModel/Item.php | 2 +- .../_files/Magento/TestModuleMSC/etc/acl.xml | 2 +- .../_files/Magento/TestModuleMSC/etc/di.xml | 2 +- .../etc/extension_attributes.xml | 2 +- .../Magento/TestModuleMSC/etc/module.xml | 2 +- .../Magento/TestModuleMSC/etc/webapi.xml | 2 +- .../Magento/TestModuleMSC/registration.php | 2 +- .../config/config-global.php.dist | 4 +- .../config/install-config-mysql.php.dist | 2 +- .../Annotation/ApiDataFixture.php | 7 +- .../Authentication/OauthHelper.php | 2 +- .../Authentication/Rest/CurlClient.php | 2 +- .../Authentication/Rest/OauthClient.php | 2 +- .../Rest/OauthClient/Signature.php | 2 +- .../Bootstrap/WebapiDocBlock.php | 2 +- .../Magento/TestFramework/Helper/Customer.php | 2 +- .../TestCase/Webapi/Adapter/Rest.php | 2 +- .../Webapi/Adapter/Rest/CurlClient.php | 2 +- .../Adapter/Rest/DocumentationGenerator.php | 2 +- .../TestCase/Webapi/Adapter/Soap.php | 2 +- .../TestCase/Webapi/AdapterInterface.php | 2 +- .../TestFramework/TestCase/Webapi/Curl.php | 2 +- .../TestFramework/TestCase/WebapiAbstract.php | 2 +- .../TestFramework/WebApiApplication.php | 2 +- .../api-functional/framework/autoload.php | 2 +- .../api-functional/framework/bootstrap.php | 2 +- dev/tests/api-functional/phpunit.xml.dist | 2 +- .../Analytics/Api/LinkProviderTest.php | 100 + .../Bundle/Api/CartItemRepositoryTest.php | 2 +- .../Bundle/Api/OrderItemRepositoryTest.php | 2 +- .../Bundle/Api/ProductLinkManagementTest.php | 3 +- .../Api/ProductOptionRepositoryTest.php | 2 +- .../Bundle/Api/ProductOptionTypeListTest.php | 2 +- .../Magento/Bundle/Api/ProductServiceTest.php | 9 +- .../Api/AttributeSetManagementTest.php | 2 +- .../Api/AttributeSetRepositoryTest.php | 2 +- .../Catalog/Api/BasePriceStorageTest.php | 160 + .../Catalog/Api/CartItemRepositoryTest.php | 2 +- ...AttributeOptionManagementInterfaceTest.php | 2 +- .../Api/CategoryAttributeRepositoryTest.php | 2 +- .../Api/CategoryLinkManagementTest.php | 2 +- .../Api/CategoryLinkRepositoryTest.php | 24 +- .../Magento/Catalog/Api/CategoryListTest.php | 2 +- .../Catalog/Api/CategoryManagementTest.php | 2 +- .../Catalog/Api/CategoryRepositoryTest.php | 2 +- .../Magento/Catalog/Api/CostStorageTest.php | 189 + .../Catalog/Api/OrderItemRepositoryTest.php | 2 +- .../ProductAttributeGroupRepositoryTest.php | 2 +- .../Api/ProductAttributeManagementTest.php | 2 +- ...uteMediaGalleryManagementInterfaceTest.php | 51 +- ...AttributeOptionManagementInterfaceTest.php | 2 +- .../Api/ProductAttributeRepositoryTest.php | 7 +- .../Api/ProductAttributeTypesListTest.php | 2 +- .../ProductCustomAttributeWrongTypeTest.php | 2 +- .../Api/ProductCustomOptionRepositoryTest.php | 14 +- .../Api/ProductCustomOptionTypeListTest.php | 2 +- .../ProductLinkManagementInterfaceTest.php | 2 +- .../ProductLinkRepositoryInterfaceTest.php | 2 +- .../Catalog/Api/ProductLinkTypeListTest.php | 2 +- .../ProductMediaAttributeManagementTest.php | 2 +- .../Api/ProductRepositoryInterfaceTest.php | 81 +- .../ProductRepositoryMultiCurrencyTest.php | 120 + .../Api/ProductRepositoryMultiStoreTest.php | 2 +- .../Api/ProductTierPriceManagementTest.php | 2 +- .../Catalog/Api/ProductTypeListTest.php | 2 +- .../Catalog/Api/SpecialPriceStorageTest.php | 148 + .../Catalog/Api/TierPriceStorageTest.php | 272 + .../Catalog/Api/_files/product_options.php | 2 +- .../Api/_files/product_options_negative.php | 2 +- .../product_options_update_negative.php | 2 +- .../Api/LowStockItemsTest.php | 2 +- .../Api/ProductRepositoryInterfaceTest.php | 2 +- .../CatalogInventory/Api/StockItemTest.php | 2 +- .../CatalogInventory/Api/StockStatusTest.php | 2 +- .../Api/CheckoutAgreementsRepositoryTest.php | 2 +- .../Magento/Cms/Api/BlockRepositoryTest.php | 2 +- .../Magento/Cms/Api/PageRepositoryTest.php | 2 +- .../Api/CartItemRepositoryTest.php | 2 +- .../Api/ConfigurableProductManagementTest.php | 2 +- .../Api/LinkManagementTest.php | 175 +- .../Api/OptionRepositoryTest.php | 2 +- .../Api/OrderItemRepositoryTest.php | 2 +- .../Api/ProductRepositoryTest.php | 2 +- .../AccountManagementCustomAttributesTest.php | 2 +- .../Customer/Api/AccountManagementMeTest.php | 2 +- .../Customer/Api/AccountManagementTest.php | 2 +- .../Customer/Api/AddressMetadataTest.php | 2 +- .../Customer/Api/AddressRepositoryTest.php | 2 +- .../Customer/Api/CustomerMetadataTest.php | 2 +- .../Customer/Api/CustomerRepositoryTest.php | 2 +- .../Customer/Api/GroupManagementTest.php | 2 +- .../Customer/Api/GroupRepositoryTest.php | 2 +- .../Api/CountryInformationAcquirerTest.php | 2 +- .../Api/CurrencyInformationAcquirerTest.php | 2 +- .../Api/CartItemRepositoryTest.php | 2 +- .../Downloadable/Api/LinkRepositoryTest.php | 2 +- .../Api/OrderItemRepositoryTest.php | 2 +- .../Api/ProductRepositoryTest.php | 2 +- .../Downloadable/Api/SampleRepositoryTest.php | 2 +- .../Eav/Api/AttributeSetManagementTest.php | 2 +- .../Eav/Api/AttributeSetRepositoryTest.php | 2 +- .../Framework/Api/Search/SearchTest.php | 2 +- .../Framework/Model/Entity/HydratorTest.php | 2 +- .../Framework/Stdlib/CookieManagerTest.php | 2 +- .../GiftMessage/Api/CartRepositoryTest.php | 2 +- .../Api/GuestCartRepositoryTest.php | 2 +- .../Api/GuestItemRepositoryTest.php | 2 +- .../GiftMessage/Api/ItemRepositoryTest.php | 2 +- .../Api/ProductLinkManagementTest.php | 2 +- .../Api/ProductLinkRepositoryTest.php | 2 +- .../Api/ProductLinkTypeListTest.php | 2 +- .../Api/ProductRepositoryInterfaceTest.php | 2 +- .../Model/AdminTokenServiceTest.php | 2 +- .../Model/CustomerTokenServiceTest.php | 2 +- .../Integration/Model/IntegrationTest.php | 2 +- .../Api/BillingAddressManagementTest.php | 2 +- .../Quote/Api/CartItemRepositoryTest.php | 2 +- .../Magento/Quote/Api/CartManagementTest.php | 10 +- .../Magento/Quote/Api/CartRepositoryTest.php | 2 +- .../Quote/Api/CartTotalRepositoryTest.php | 2 +- .../Quote/Api/CouponManagementTest.php | 2 +- .../Api/GuestBillingAddressManagementTest.php | 30 +- .../Quote/Api/GuestCartItemRepositoryTest.php | 2 +- .../Quote/Api/GuestCartManagementTest.php | 2 +- .../Quote/Api/GuestCartRepositoryTest.php | 2 +- .../Api/GuestCartTotalRepositoryTest.php | 20 +- .../Quote/Api/GuestCouponManagementTest.php | 2 +- .../Api/GuestPaymentMethodManagementTest.php | 2 +- .../Quote/Api/GuestShipmentEstimationTest.php | 2 +- .../Api/GuestShippingMethodManagementTest.php | 2 +- .../Quote/Api/PaymentMethodManagementTest.php | 2 +- .../Api/ShippingMethodManagementTest.php | 7 +- .../Service/V1/CreditMemoCreateRefundTest.php | 2 +- .../Service/V1/CreditmemoAddCommentTest.php | 2 +- .../Sales/Service/V1/CreditmemoCancelTest.php | 8 +- .../Service/V1/CreditmemoCommentsListTest.php | 2 +- .../Sales/Service/V1/CreditmemoCreateTest.php | 2 +- .../Sales/Service/V1/CreditmemoEmailTest.php | 2 +- .../Sales/Service/V1/CreditmemoGetTest.php | 2 +- .../Sales/Service/V1/CreditmemoListTest.php | 2 +- .../Service/V1/InvoiceAddCommentTest.php | 2 +- .../Sales/Service/V1/InvoiceCaptureTest.php | 2 +- .../Service/V1/InvoiceCommentsListTest.php | 2 +- .../Sales/Service/V1/InvoiceCreateTest.php | 2 +- .../Sales/Service/V1/InvoiceEmailTest.php | 2 +- .../Sales/Service/V1/InvoiceGetTest.php | 2 +- .../Sales/Service/V1/InvoiceListTest.php | 2 +- .../Sales/Service/V1/InvoiceVoidTest.php | 2 +- .../Service/V1/OrderAddressUpdateTest.php | 2 +- .../Sales/Service/V1/OrderCancelTest.php | 2 +- .../Service/V1/OrderCommentsListTest.php | 2 +- .../Sales/Service/V1/OrderCreateTest.php | 65 +- .../Sales/Service/V1/OrderEmailTest.php | 2 +- .../Sales/Service/V1/OrderGetStatusTest.php | 2 +- .../Magento/Sales/Service/V1/OrderGetTest.php | 2 +- .../Sales/Service/V1/OrderHoldTest.php | 2 +- .../Service/V1/OrderInvoiceCreateTest.php | 2 +- .../Sales/Service/V1/OrderItemGetListTest.php | 2 +- .../Sales/Service/V1/OrderItemGetTest.php | 2 +- .../Sales/Service/V1/OrderListTest.php | 2 +- .../Service/V1/OrderStatusHistoryAddTest.php | 2 +- .../Sales/Service/V1/OrderUnHoldTest.php | 2 +- .../Sales/Service/V1/RefundOrderTest.php | 2 +- .../Sales/Service/V1/ShipOrderTest.php | 2 +- .../Service/V1/ShipmentAddCommentTest.php | 2 +- .../Sales/Service/V1/ShipmentAddTrackTest.php | 2 +- .../Service/V1/ShipmentCommentsListTest.php | 2 +- .../Sales/Service/V1/ShipmentCreateTest.php | 2 +- .../Sales/Service/V1/ShipmentEmailTest.php | 2 +- .../Sales/Service/V1/ShipmentGetTest.php | 2 +- .../Sales/Service/V1/ShipmentLabelGetTest.php | 2 +- .../Sales/Service/V1/ShipmentListTest.php | 2 +- .../Service/V1/ShipmentRemoveTrackTest.php | 2 +- .../Sales/Service/V1/TransactionTest.php | 2 +- .../V1/ReturnItemsAfterRefundOrderTest.php | 114 + .../SalesRule/Api/CouponManagementTest.php | 2 +- .../SalesRule/Api/CouponRepositoryTest.php | 2 +- .../SalesRule/Api/RuleRepositoryTest.php | 2 +- .../Magento/Store/Api/GroupRepositoryTest.php | 2 +- .../Store/Api/StoreConfigManagerTest.php | 2 +- .../Magento/Store/Api/StoreRepositoryTest.php | 2 +- .../Store/Api/WebsiteRepositoryTest.php | 2 +- .../Tax/Api/TaxClassRepositoryTest.php | 2 +- .../Magento/Tax/Api/TaxRateRepositoryTest.php | 2 +- .../Api/TaxRuleRepositoryInterfaceTest.php | 2 +- .../Webapi/Authentication/RestTest.php | 2 +- .../CustomAttributeTypeWsdlGenerationTest.php | 2 +- .../ServiceSerializationTest.php | 2 +- .../Magento/Webapi/DeserializationTest.php | 2 +- .../Magento/Webapi/JoinDirectivesTest.php | 2 +- .../JsonGenerationFromDataObjectTest.php | 2 +- .../Magento/Webapi/PartialResponseTest.php | 2 +- .../Magento/Webapi/Routing/BaseService.php | 2 +- .../Webapi/Routing/CoreRoutingTest.php | 2 +- .../Magento/Webapi/Routing/GettersTest.php | 2 +- .../Webapi/Routing/NoWebApiXmlTest.php | 2 +- .../Webapi/Routing/RequestIdOverrideTest.php | 2 +- .../Webapi/Routing/RestErrorHandlingTest.php | 2 +- .../Webapi/Routing/ServiceVersionV1Test.php | 2 +- .../Webapi/Routing/ServiceVersionV2Test.php | 2 +- .../Webapi/Routing/SoapErrorHandlingTest.php | 2 +- .../Magento/Webapi/Routing/SubsetTest.php | 2 +- .../WsdlGenerationFromDataObjectTest.php | 2 +- dev/tests/functional/.htaccess.sample | 4 +- dev/tests/functional/bootstrap.php | 2 +- dev/tests/functional/composer.json | 4 +- dev/tests/functional/credentials.xml.dist | 47 +- dev/tests/functional/etc/config.xml.dist | 2 +- dev/tests/functional/etc/config.xsd | 2 +- dev/tests/functional/etc/di.xml | 104 +- dev/tests/functional/etc/events.xml | 2 +- dev/tests/functional/etc/events.xsd | 4 +- .../functional/etc/repository_replacer.xml | 2 +- .../etc/repository_replacer_payments.xml | 17 + dev/tests/functional/isolation.php | 2 +- .../Magento/Mtf/App/State/AbstractState.php | 35 +- .../lib/Magento/Mtf/App/State/State1.php | 29 +- .../Mtf/App/State/StateHandlerInterface.php | 21 + .../Mtf/Client/Element/ConditionsElement.php | 34 +- .../Mtf/Client/Element/DatepickerElement.php | 4 +- .../Element/DropdownmultiselectElement.php | 2 +- .../Client/Element/GlobalsearchElement.php | 2 +- .../Mtf/Client/Element/JquerytreeElement.php | 2 +- .../Client/Element/LiselectstoreElement.php | 4 +- .../Element/MultiselectgrouplistElement.php | 2 +- .../Client/Element/MultiselectlistElement.php | 2 +- .../Client/Element/MultisuggestElement.php | 2 +- .../Client/Element/OptgroupselectElement.php | 2 +- .../Mtf/Client/Element/RadiobuttonElement.php | 2 +- .../Mtf/Client/Element/SelectstoreElement.php | 2 +- .../Element/SimplifiedselectElement.php | 2 +- .../Mtf/Client/Element/SuggestElement.php | 15 +- .../Mtf/Client/Element/SwitcherElement.php | 2 +- .../lib/Magento/Mtf/Client/Element/Tree.php | 2 +- .../Mtf/Client/Element/TreeElement.php | 2 +- .../Mtf/Config/FileResolver/ScopeConfig.php | 27 + .../Mtf/Constraint/AbstractAssertForm.php | 6 +- .../lib/Magento/Mtf/EntryPoint/EntryPoint.php | 2 +- .../lib/Magento/Mtf/Handler/Webapi.php | 2 +- .../lib/Magento/Mtf/Page/BackendPage.php | 2 +- .../Mtf/System/Observer/WebapiResponse.php | 2 +- .../Mtf/Troubleshooting/AdminAnalyzer.php | 140 + .../Mtf/Troubleshooting/ConfigAnalyzer.php | 140 + .../Mtf/Troubleshooting/Configuration.php | 80 + .../Mtf/Troubleshooting/GlobalAnalyzer.php | 61 + .../Troubleshooting/Helper/UrlAnalyzer.php | 70 + .../Mtf/Troubleshooting/HtaccessAnalyzer.php | 97 + .../Mtf/Troubleshooting/PhpUnitAnalyzer.php | 82 + .../SeleniumSessionAnalyzer.php | 79 + .../StaticClassesGenerator.php | 68 + .../Troubleshooting/StorefrontAnalyzer.php | 123 + .../lib/Magento/Mtf/Util/Command/Cli.php | 6 +- .../Magento/Mtf/Util/Command/Cli/Cache.php | 11 +- .../lib/Magento/Mtf/Util/Command/Cli/Cron.php | 2 +- .../Magento/Mtf/Util/Command/Cli/Indexer.php | 57 + .../Magento/Mtf/Util/Command/Cli/Queue.php | 2 +- .../Magento/Mtf/Util/Command/Cli/Setup.php | 52 + .../Mtf/Util/Command/Cli/StaticContent.php | 30 + .../Magento/Mtf/Util/Command/File/Export.php | 131 + .../Mtf/Util/Command/File/Export/Data.php | 58 + .../Mtf/Util/Command/File/Export/Reader.php | 89 + .../Command/File/Export/ReaderInterface.php | 25 + .../Mtf/Util/Command/File/ExportInterface.php | 46 + .../lib/Magento/Mtf/Util/Command/File/Log.php | 62 + .../Mtf/Util/Command/GeneratedCode.php | 49 + .../Magento/Mtf/Util/Command/PathChecker.php | 52 + .../lib/Magento/Mtf/Util/Command/Website.php | 4 +- .../Mtf/Util/Filesystem/FileHelper.php | 103 + .../lib/Magento/Mtf/Util/Generate/Factory.php | 2 +- .../Util/Generate/Factory/AbstractFactory.php | 2 +- .../Mtf/Util/Generate/Factory/Block.php | 2 +- .../Mtf/Util/Generate/Factory/Fixture.php | 2 +- .../Mtf/Util/Generate/Factory/Handler.php | 2 +- .../Mtf/Util/Generate/Factory/Page.php | 2 +- .../Mtf/Util/Generate/Factory/Repository.php | 2 +- .../Mtf/Util/Generate/File/Generator.php | 64 + .../Util/Generate/File/TemplateInterface.php | 26 + .../Util/Generate/Fixture/FieldsProvider.php | 2 +- .../Mtf/Util/Generate/Fixture/SchemaXml.php | 2 +- .../Mtf/Util/Generate/Fixture/template.xml | 2 +- .../Repository/RepositoryResource.php | 2 +- .../Generate/Repository/TableCollection.php | 2 +- .../Util/ModuleResolver/SequenceSorter.php | 2 +- .../CurlTransport/BackendDecorator.php | 2 +- .../CurlTransport/FrontendDecorator.php | 2 +- .../CurlTransport/WebapiDecorator.php | 2 +- dev/tests/functional/phpunit.xml.dist | 3 +- .../Test/Block/System/Messages.php | 2 +- .../Test/Block/System/Messages/System.php | 2 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- ...AssertImportCheckDataErrorMessagesList.php | 91 + .../Test/TestCase/ImportDataNegativeTest.xml | 41 + .../template/pricing/advanced_incorrect.php | 14 + .../Test/etc/di.xml | 15 + .../Mtf/App/State/NotificationTimeHandler.php | 55 + .../Dashboard/Analytics/SubscriptionBlock.php | 42 + .../Adminhtml/Dashboard/Page/Actions.php | 31 + .../Block/System/Config/AnalyticsForm.php | 99 + .../AssertAcceptSubscriptionPopup.php | 42 + .../Test/Constraint/AssertBasicTierLink.php | 50 + .../AssertConfigAnalyticsDisabled.php | 49 + .../AssertConfigAnalyticsEnabled.php | 50 + .../AssertConfigAnalyticsRestored.php | 54 + .../AssertConfigAnalyticsVerticalScope.php | 39 + .../AssertDeclineSubscriptionPopup.php | 42 + .../AssertEmptyVerticalCanNotBeSaved.php | 42 + .../Test/Constraint/AssertFreeTierLink.php | 50 + .../AssertSkipSubscriptionPopup.php | 42 + .../AssertSubscriptionPopupNotExist.php | 39 + .../Test/Constraint/AssertVerticalIsSet.php | 42 + .../Test/Page/Adminhtml/ConfigAnalytics.xml | 13 + .../Test/Page/Adminhtml/Dashboard.xml | 14 + .../Analytics/Test/Repository/Role.xml | 19 + .../Analytics/Test/Repository/User.xml | 24 + ...lyticsSubscriptionCheckPermissionsTest.php | 37 + ...lyticsSubscriptionCheckPermissionsTest.xml | 15 + .../Test/TestCase/FreeTierButtonTest.php | 36 + .../Test/TestCase/FreeTierButtonTest.xml | 15 + .../Analytics/Test/TestCase/InstallTest.xml | 28 + .../Test/TestCase/NavigateMenuTest.xml | 17 + .../Test/TestCase/SetVerticalTest.php | 40 + .../Test/TestCase/SetVerticalTest.xml | 21 + .../Test/TestStep/OpenAnalyticsConfigStep.php | 54 + .../app/Magento/Analytics/Test/etc/di.xml | 51 + .../Magento/Analytics/Test/etc/testcase.xml | 14 + .../Authorizenet/Test/Block/Form/Cc.php | 18 - .../Authorizenet/Test/Block/Form/Cc.xml | 20 - .../Test/Fixture/CreditCardAuthorizenet.xml | 20 - .../Test/Repository/ConfigData.xml | 34 +- .../Test/TestCase/OnePageCheckoutTest.xml | 19 +- .../Backend/Test/Block/Admin/Login.php | 2 +- .../Backend/Test/Block/Admin/Login.xml | 2 +- .../Test/Block/Dashboard/StoreStats.php | 2 +- .../Test/Block/Dashboard/StoreStats.xml | 2 +- .../Test/Block/Dashboard/Tab/Products.php | 2 +- .../Block/Dashboard/Tab/Products/Ordered.php | 2 +- .../app/Magento/Backend/Test/Block/Denied.php | 2 +- .../Backend/Test/Block/FormPageActions.php | 2 +- .../Backend/Test/Block/GridPageActions.php | 2 +- .../app/Magento/Backend/Test/Block/Menu.php | 36 +- .../Magento/Backend/Test/Block/Messages.php | 40 +- .../Magento/Backend/Test/Block/Page/Error.php | 2 +- .../Backend/Test/Block/Page/Header.php | 2 +- .../Magento/Backend/Test/Block/Page/Main.php | 2 +- .../Backend/Test/Block/PageActions.php | 2 +- .../Backend/Test/Block/System/Config/Form.php | 57 +- .../Test/Block/System/Config/Form/Group.php | 20 +- .../Test/Block/System/Config/PageActions.php | 2 +- .../Test/Block/System/Config/Payments.php | 2 +- .../Backend/Test/Block/System/Config/Tabs.php | 60 + .../Test/Block/System/Store/Delete/Form.php | 2 +- .../Test/Block/System/Store/Delete/Form.xml | 2 +- .../System/Store/Edit/Form/GroupForm.php | 2 +- .../System/Store/Edit/Form/GroupForm.xml | 2 +- .../System/Store/Edit/Form/StoreForm.php | 21 +- .../System/Store/Edit/Form/StoreForm.xml | 2 +- .../System/Store/Edit/Form/WebsiteForm.php | 2 +- .../System/Store/Edit/Form/WebsiteForm.xml | 2 +- .../Block/System/Store/FormPageActions.php | 2 +- .../Block/System/Store/GridPageActions.php | 2 +- .../Test/Block/System/Store/StoreGrid.php | 2 +- .../Magento/Backend/Test/Block/Template.php | 4 +- .../Magento/Backend/Test/Block/Version.php | 2 +- .../Backend/Test/Block/Widget/FormTabs.php | 2 +- .../Backend/Test/Block/Widget/Grid.php | 2 +- .../Magento/Backend/Test/Block/Widget/Tab.php | 2 +- .../AssertAdminLoginPageIsAvailable.php | 2 +- .../AssertBackendPageIsAvailable.php | 3 +- .../AssertBestsellersOnDashboard.php | 2 +- .../AssertDeveloperSectionVisibility.php | 46 + .../AssertGlobalSearchCustomerName.php | 2 +- .../AssertGlobalSearchNoRecordsFound.php | 2 +- .../Constraint/AssertGlobalSearchOrderId.php | 2 +- .../Constraint/AssertGlobalSearchPreview.php | 2 +- .../AssertGlobalSearchProductName.php | 2 +- .../Constraint/AssertHttpUsedOnFrontend.php | 91 + .../AssertHttpsHeaderOptionsAvailable.php | 2 +- .../AssertHttpsHeaderOptionsNotAvailable.php | 2 +- .../Constraint/AssertHttpsUsedOnBackend.php | 101 + .../Constraint/AssertLocaleCodeVisibility.php | 46 + .../Constraint/AssertMenuItemNotVisible.php | 43 + .../Constraint/AssertStoreCanBeLocalized.php | 2 +- .../Backend/Test/Fixture/Admin/SuperAdmin.php | 2 +- .../Backend/Test/Fixture/GlobalSearch.xml | 2 +- .../Test/Fixture/GlobalSearch/Query.php | 2 +- .../Backend/Test/Fixture/Source/Date.php | 31 +- .../Backend/Test/Handler/Conditions.php | 2 +- .../Backend/Test/Handler/Extractor.php | 2 +- .../Backend/Test/Handler/Ui/LoginUser.php | 2 +- .../Backend/Test/Handler/Ui/LogoutUser.php | 2 +- .../Backend/Test/Page/AdminAuthLogin.php | 2 +- .../Backend/Test/Page/Adminhtml/Dashboard.xml | 3 +- .../Test/Page/Adminhtml/DeleteGroup.xml | 2 +- .../Test/Page/Adminhtml/DeleteWebsite.xml | 2 +- .../Backend/Test/Page/Adminhtml/EditGroup.xml | 2 +- .../Backend/Test/Page/Adminhtml/EditStore.xml | 2 +- .../Test/Page/Adminhtml/EditWebsite.xml | 2 +- .../Test/Page/Adminhtml/NewGroupIndex.xml | 2 +- .../Test/Page/Adminhtml/NewWebsiteIndex.xml | 2 +- .../Test/Page/Adminhtml/StoreDelete.xml | 2 +- .../Test/Page/Adminhtml/StoreIndex.xml | 2 +- .../Backend/Test/Page/Adminhtml/StoreNew.xml | 2 +- .../Test/Page/Adminhtml/SystemConfig.xml | 2 +- .../Test/Page/Adminhtml/SystemConfigEdit.xml | 3 +- .../SystemConfigEditSectionPayment.xml | 2 +- .../Backend/Test/Repository/ConfigData.xml | 136 +- .../TestCase/ConfigPageVisibilityTest.php | 49 + .../TestCase/ConfigPageVisibilityTest.xml | 16 + .../Test/TestCase/ConfigureSecureUrlsTest.php | 160 + .../Test/TestCase/ConfigureSecureUrlsTest.xml | 17 + .../Test/TestCase/ExpireAdminSessionTest.php | 2 +- .../Test/TestCase/ExpireAdminSessionTest.xml | 2 +- .../Test/TestCase/GlobalSearchEntityTest.php | 2 +- .../Test/TestCase/GlobalSearchEntityTest.xml | 7 +- .../Test/TestCase/HttpsHeadersDisableTest.php | 2 +- .../Test/TestCase/HttpsHeadersDisableTest.xml | 2 +- .../Test/TestCase/HttpsHeadersEnableTest.php | 2 +- .../Test/TestCase/HttpsHeadersEnableTest.xml | 2 +- .../Test/TestCase/NavigateMenuTest.php | 7 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../Test/TestStep/OpenDashboardStep.php | 40 + .../tests/app/Magento/Backend/Test/etc/di.xml | 22 +- .../Test/Block/Adminhtml/BackupGrid.php | 2 +- .../Test/Constraint/AssertBackupInGrid.php | 2 +- .../Test/Page/Adminhtml/BackupIndex.xml | 2 +- .../Backup/Test/TestCase/NavigateMenuTest.xml | 2 +- .../Test/Block/Adminhtml/Report/Grid.php | 2 +- .../Braintree/Test/Block/Form/BraintreeCc.php | 95 + .../Block/Form/{Cc.xml => BraintreeCc.xml} | 20 +- .../Magento/Braintree/Test/Block/Form/Cc.php | 55 - .../Braintree/Test/Block/Form/Secure3d.php | 2 +- .../Braintree/Test/Block/Form/Secure3d.xml | 4 +- .../app/Magento/Braintree/Test/Block/Info.php | 38 - .../Test/Block/Paypal/PopupWindow.php | 2 +- .../Test/Block/System/Config/Braintree.php | 2 +- .../Assert3dSecureInfoIsPresent.php | 13 +- ...editCardJsValidationMessagesArePresent.php | 46 + ...tDeviceDataIsPresentInBraintreeRequest.php | 52 + ...TransactionIsPresentInSettlementReport.php | 21 +- ...ntree.xml => BraintreeSandboxCustomer.xml} | 17 +- .../Test/Fixture/Secure3dBraintree.xml | 2 +- .../Adminhtml/BraintreeSettlementReport.xml | 2 +- .../Test/Page/Adminhtml/SalesOrderView.xml | 12 - .../SystemConfigEditSectionPayment.xml | 2 +- .../Test/Page/CatalogProductView.xml | 2 +- .../Braintree/Test/Page/CheckoutCart.xml | 2 +- .../Braintree/Test/Page/CheckoutOnepage.xml | 4 +- .../Repository/BraintreeSandboxCustomer.xml | 17 + .../Braintree/Test/Repository/ConfigData.xml | 220 +- .../Braintree/Test/Repository/CreditCard.xml | 44 +- .../Braintree/Test/Repository/Secure3d.xml | 2 +- .../BraintreeSettlementReportTest.php | 3 +- .../BraintreeSettlementReportTest.xml | 10 +- .../CheckoutWithBraintreePaypalCartTest.php | 3 +- .../CheckoutWithBraintreePaypalCartTest.xml | 6 +- ...heckoutWithBraintreePaypalMinicartTest.php | 3 +- ...heckoutWithBraintreePaypalMinicartTest.xml | 18 +- ...ateOnlineCreditMemoBraintreePaypalTest.php | 3 +- ...ateOnlineCreditMemoBraintreePaypalTest.xml | 49 +- .../CreateOnlineCreditMemoBraintreeTest.php | 40 + .../CreateOnlineCreditMemoBraintreeTest.xml | 102 + .../CreateOnlineInvoiceEntityTest.xml | 16 +- .../Test/TestCase/CreateOrderBackendTest.xml | 38 +- ...derWithPayPalBraintreeVaultBackendTest.php | 56 + ...derWithPayPalBraintreeVaultBackendTest.xml | 32 + .../TestCase/CreateVaultOrderBackendTest.xml | 9 +- .../TestCase/InvoicePayPalBraintreeTest.php | 5 +- .../TestCase/InvoicePaypalBraintreeTest.xml | 23 +- .../OnePageCheckoutAcceptPaymentTest.php | 18 +- .../OnePageCheckoutAcceptPaymentTest.xml | 8 +- .../TestCase/OnePageCheckoutDeclinedTest.xml | 71 + .../OnePageCheckoutDenyPaymentTest.php | 17 +- .../OnePageCheckoutDenyPaymentTest.xml | 8 +- ...nePageCheckoutFromMiniShoppingCartTest.xml | 62 + .../Test/TestCase/OnePageCheckoutTest.xml | 41 +- .../OnePageCheckoutWith3dSecureFailedTest.php | 49 + .../OnePageCheckoutWith3dSecureFailedTest.xml | 27 + .../OnePageCheckoutWith3dSecureTest.php | 3 +- .../OnePageCheckoutWith3dSecureTest.xml | 5 +- ...OnePageCheckoutWithBraintreePaypalTest.php | 3 +- ...OnePageCheckoutWithBraintreePaypalTest.xml | 22 +- .../OnePageCheckoutWithDiscountTest.xml | 30 +- .../Test/TestCase/ReorderUsingVaultTest.xml | 12 +- ...veUseDeleteVaultForPaypalBraintreeTest.php | 3 +- ...veUseDeleteVaultForPaypalBraintreeTest.xml | 5 +- .../Test/TestCase/UseVaultOnCheckoutTest.xml | 9 +- .../UseVaultWith3dSecureOnCheckoutTest.php | 3 +- .../UseVaultWith3dSecureOnCheckoutTest.xml | 6 +- .../VerifyPaymentMethodOnCheckoutTest.xml | 23 + .../Test/TestStep/AcceptPaymentStep.php | 2 +- .../ChangeOrderStatusToPaymentReviewStep.php | 2 +- .../TestStep/CheckBraintreeConfigStep.php | 2 +- .../CheckoutWithPaypalFromCartStep.php | 2 +- .../CheckoutWithPaypalFromMinicartStep.php | 2 +- .../Test/TestStep/ContinueToPaypalStep.php | 2 +- .../Test/TestStep/DenyPaymentStep.php | 2 +- .../PlaceOrderWith3dSecureFailedStep.php | 55 + .../TestStep/PlaceOrderWith3dSecureStep.php | 2 +- .../TestStep/PlaceOrderWithPaypalStep.php | 59 +- .../Test/TestStep/SettleTransactionStep.php | 93 + .../app/Magento/Braintree/Test/etc/di.xml | 16 +- .../Magento/Braintree/Test/etc/testcase.xml | 54 +- .../Catalog/Product/Edit/Section/Bundle.php | 60 +- .../Product/Edit/Section/Bundle/Option.php | 2 +- .../Product/Edit/Section/Bundle/Option.xml | 2 +- .../Section/Bundle/Option/Search/Grid.php | 2 +- .../Edit/Section/Bundle/Option/Selection.php | 2 +- .../Edit/Section/Bundle/Option/Selection.xml | 6 +- .../Adminhtml/Product/Composite/Configure.php | 2 +- .../Adminhtml/Product/Composite/Configure.xml | 2 +- .../Block/Adminhtml/Product/ProductForm.xml | 2 +- .../Test/Block/Catalog/Product/View.php | 41 +- .../Block/Catalog/Product/View/Summary.php | 52 + .../Product/View/Summary/ConfiguredPrice.php | 35 + .../Catalog/Product/View/Type/Bundle.php | 109 +- .../Catalog/Product/View/Type/Option.php | 2 +- .../Product/View/Type/Option/Checkbox.php | 2 +- .../Product/View/Type/Option/Checkbox.xml | 2 +- .../Product/View/Type/Option/Dropdown.php | 2 +- .../Product/View/Type/Option/Dropdown.xml | 2 +- .../Product/View/Type/Option/Element/Qty.php | 37 + .../Product/View/Type/Option/Hidden.php | 17 + .../Product/View/Type/Option/Hidden.xml | 15 + .../Product/View/Type/Option/Multiple.php | 2 +- .../Product/View/Type/Option/Multiple.xml | 2 +- .../Product/View/Type/Option/Radiobuttons.php | 2 +- .../Product/View/Type/Option/Radiobuttons.xml | 2 +- .../Constraint/AssertBundleInCategory.php | 2 +- .../AssertBundleItemsOnProductPage.php | 39 +- .../AssertBundleItemsSummaryOnProductPage.php | 102 + .../AssertBundleOptionTitleOnStorefront.php | 68 + .../Constraint/AssertBundleOptionsDeleted.php | 78 + ...sertBundlePriceCalculatedOnProductPage.php | 64 + .../Test/Constraint/AssertBundlePriceType.php | 2 +- .../Test/Constraint/AssertBundlePriceView.php | 2 +- .../Constraint/AssertBundleProductForm.php | 3 +- .../Constraint/AssertBundleProductInCart.php | 2 +- ...ProductInCustomerWishlistOnBackendGrid.php | 2 +- .../Constraint/AssertBundleProductPage.php | 2 +- ...roductCustomOptionsOnBundleProductPage.php | 2 +- .../AssertTierPriceOnBundleProductPage.php | 2 +- .../Bundle/Test/Fixture/BundleProduct.xml | 2 +- .../BundleProduct/BundleSelections.php | 12 +- .../Magento/Bundle/Test/Fixture/Cart/Item.php | 84 +- .../BundleProduct/BundleProductInterface.php | 2 +- .../Test/Handler/BundleProduct/Curl.php | 6 +- .../Test/Handler/BundleProduct/Webapi.php | 54 +- .../Test/Page/Adminhtml/CustomerIndexEdit.xml | 2 +- .../Test/Page/Adminhtml/OrderCreateIndex.xml | 2 +- .../Test/Page/Product/CatalogProductView.xml | 2 +- .../Bundle/Test/Repository/BundleProduct.xml | 156 +- .../BundleProduct/BundleSelections.xml | 403 +- .../Repository/BundleProduct/CheckoutData.xml | 100 +- .../Test/Repository/BundleProduct/Price.xml | 2 +- .../TestCase/BundleOptionsSummaryTest.php | 34 + .../TestCase/BundleOptionsSummaryTest.xml | 17 + .../CreateBundleProductEntityTest.php | 2 +- .../CreateBundleProductEntityTest.xml | 32 +- .../Test/TestCase/DeleteProductEntityTest.xml | 2 +- .../DeleteProductFromMiniShoppingCartTest.xml | 2 +- ...ecentlyComparedProductsOnOrderPageTest.xml | 17 + .../Test/TestCase/UpdateBundleOptionsTest.php | 94 + .../Test/TestCase/UpdateBundleOptionsTest.xml | 19 + .../UpdateBundleProductEntityTest.php | 66 +- .../UpdateBundleProductEntityTest.xml | 9 +- .../ValidateOrderOfProductTypeTest.xml | 2 +- .../app/Magento/Bundle/Test/etc/curl/di.xml | 2 +- .../tests/app/Magento/Bundle/Test/etc/di.xml | 27 + .../app/Magento/Bundle/Test/etc/webapi/di.xml | 2 +- .../Test/Block/Adminhtml/LoginWithCaptcha.php | 50 + .../Test/Block/Adminhtml/LoginWithCaptcha.xml | 21 + .../Test/Block/Form/LoginWithCaptcha.php | 61 + .../Test/Block/Form/LoginWithCaptcha.xml | 19 + .../AssertCaptchaFieldOnBackend.php | 45 + .../AssertCaptchaFieldOnStorefront.php | 45 + .../Magento/Captcha/Test/Fixture/Customer.xml | 12 + .../app/Magento/Captcha/Test/Fixture/User.xml | 12 + .../Adminhtml/AdminAuthLoginWithCaptcha.xml | 12 + .../Test/Page/CustomerAccountLogin.xml | 12 + .../Captcha/Test/Repository/ConfigData.xml | 86 + .../Test/TestCase/CaptchaOnAdminLoginTest.php | 126 + .../Test/TestCase/CaptchaOnAdminLoginTest.xml | 18 + .../TestCase/CaptchaOnStoreFrontLoginTest.php | 135 + .../TestCase/CaptchaOnStoreFrontLoginTest.xml | 17 + .../Test/Block/AbstractConfigureBlock.php | 2 +- .../Catalog/Test/Block/AbstractPriceBlock.php | 2 +- .../Adminhtml/Category/Edit/CategoryForm.php | 3 +- .../Adminhtml/Category/Edit/CategoryForm.xml | 12 +- .../Adminhtml/Category/Edit/PageActions.php | 41 +- .../Category/Edit/Section/ProductGrid.php | 2 +- .../Category/Edit/Section/Products.php | 2 +- .../Test/Block/Adminhtml/Category/Tree.php | 32 +- .../Adminhtml/Category/Widget/Chooser.php | 2 +- .../Product/Attribute/AttributeForm.php | 2 +- .../Product/Attribute/AttributeForm.xml | 2 +- .../Product/Attribute/CustomAttribute.php | 4 +- .../Product/Attribute/Edit/AttributeForm.php | 2 +- .../Product/Attribute/Edit/AttributeForm.xml | 2 +- .../Product/Attribute/Edit/Options.php | 2 +- .../Product/Attribute/Edit/Tab/Advanced.php | 2 +- .../Product/Attribute/Edit/Tab/Options.php | 2 +- .../Attribute/Edit/Tab/Options/Option.php | 2 +- .../Attribute/Edit/Tab/Options/Option.xml | 2 +- .../Adminhtml/Product/Attribute/Grid.php | 6 +- .../Product/Attribute/Set/FormPageActions.php | 2 +- .../Adminhtml/Product/Attribute/Set/Grid.php | 2 +- .../Product/Attribute/Set/GridPageActions.php | 2 +- .../Adminhtml/Product/Attribute/Set/Main.php | 2 +- .../Attribute/Set/Main/AttributeSetForm.php | 2 +- .../Attribute/Set/Main/AttributeSetForm.xml | 2 +- .../Product/Attribute/Set/Main/EditForm.php | 2 +- .../Product/Attribute/Set/Main/EditForm.xml | 2 +- .../Adminhtml/Product/Composite/Configure.php | 2 +- .../Adminhtml/Product/Composite/Configure.xml | 2 +- .../Product/Edit/Action/Attribute.xml | 17 - .../Product/Edit/Action/FormPageActions.php | 2 +- .../Edit/Action/Tab/UpdateAttributeTab.php | 49 + .../Edit/Action/UpdateAttributeForm.php | 17 + .../Edit/Action/UpdateAttributeForm.xml | 28 + .../Edit/Section/AdvancedInventory.php | 2 +- .../Product/Edit/Section/AdvancedPricing.php | 2 +- .../Section/AdvancedPricing/OptionTier.php | 13 +- .../Section/AdvancedPricing/OptionTier.xml | 9 +- .../Product/Edit/Section/Attributes.php | 2 +- .../Product/Edit/Section/Attributes/Grid.php | 2 +- .../Edit/Section/Attributes/Search.php | 2 +- .../Product/Edit/Section/BlockGallery.php | 49 + .../Product/Edit/Section/Options.php | 2 +- .../Edit/Section/Options/AbstractOptions.php | 2 +- .../Product/Edit/Section/Options/Row.php | 2 +- .../Edit/Section/Options/Search/Grid.php | 2 +- .../Product/Edit/Section/Options/Type.php | 2 +- .../Edit/Section/Options/Type/Area.php | 2 +- .../Edit/Section/Options/Type/Area.xml | 2 +- .../Edit/Section/Options/Type/Checkbox.php | 2 +- .../Edit/Section/Options/Type/Checkbox.xml | 2 +- .../Edit/Section/Options/Type/Date.php | 2 +- .../Edit/Section/Options/Type/Date.xml | 2 +- .../Edit/Section/Options/Type/DateTime.php | 2 +- .../Edit/Section/Options/Type/DateTime.xml | 2 +- .../Edit/Section/Options/Type/DropDown.php | 2 +- .../Edit/Section/Options/Type/DropDown.xml | 2 +- .../Edit/Section/Options/Type/Field.php | 2 +- .../Edit/Section/Options/Type/Field.xml | 2 +- .../Edit/Section/Options/Type/File.php | 2 +- .../Edit/Section/Options/Type/File.xml | 2 +- .../Section/Options/Type/MultipleSelect.php | 2 +- .../Section/Options/Type/MultipleSelect.xml | 2 +- .../Section/Options/Type/RadioButtons.php | 2 +- .../Section/Options/Type/RadioButtons.xml | 2 +- .../Edit/Section/Options/Type/Time.php | 2 +- .../Edit/Section/Options/Type/Time.xml | 2 +- .../Product/Edit/Section/ProductDetails.php | 2 +- .../Section/ProductDetails/AttributeSet.php | 2 +- .../Section/ProductDetails/CategoryIds.php | 24 +- .../Section/ProductDetails/NewCategoryIds.php | 2 +- .../Section/ProductDetails/NewCategoryIds.xml | 2 +- .../Product/Edit/Section/Related.php | 2 +- .../Product/Edit/Section/Related/Grid.php | 2 +- .../Edit/Section/Websites/StoreTree.php | 2 +- .../Adminhtml/Product/FormPageActions.php | 45 +- .../Test/Block/Adminhtml/Product/Grid.php | 12 +- .../Adminhtml/Product/GridPageAction.php | 2 +- .../Adminhtml/Product/Modal/AddAttribute.php | 2 +- .../Adminhtml/Product/Modal/NewAttribute.php | 2 +- .../Block/Adminhtml/Product/ProductForm.php | 48 +- .../Block/Adminhtml/Product/ProductForm.xml | 27 +- .../Adminhtml/Product/Widget/Chooser.php | 2 +- .../Test/Block/Category/ProductPagination.php | 2 +- .../Catalog/Test/Block/Category/View.php | 2 +- .../Catalog/Test/Block/Links/CompareLink.php | 19 +- .../Catalog/Test/Block/Product/Additional.php | 2 +- .../Block/Product/Compare/ListCompare.php | 16 +- .../Test/Block/Product/Compare/Sidebar.php | 16 +- .../Product/Grouped/AssociatedProducts.php | 2 +- .../ListAssociatedProducts.php | 2 +- .../ListAssociatedProducts/Product.php | 2 +- .../AssociatedProducts/Search/Grid.php | 2 +- .../Test/Block/Product/ListProduct.php | 9 +- .../Catalog/Test/Block/Product/Price.php | 14 +- .../Product/ProductList/BottomToolbar.php | 2 +- .../Block/Product/ProductList/Crosssell.php | 2 +- .../Block/Product/ProductList/ProductItem.php | 4 +- .../Product/ProductList/PromotedSection.php | 2 +- .../Block/Product/ProductList/Related.php | 2 +- .../ProductList/Related/ProductItem.php | 2 +- .../Block/Product/ProductList/TopToolbar.php | 47 +- .../Test/Block/Product/ProductList/Upsell.php | 2 +- .../Catalog/Test/Block/Product/View.php | 95 +- .../Test/Block/Product/View/CustomOptions.php | 4 +- .../Test/Block/Product/View/CustomOptions.xml | 2 +- .../app/Magento/Catalog/Test/Block/Search.php | 10 +- .../AssertAbsenceDeleteAttributeButton.php | 2 +- .../AssertAddToCartButtonAbsent.php | 2 +- .../AssertAddToCartButtonPresent.php | 2 +- ...sertAddedProductAttributeOnProductForm.php | 16 +- ...AssertAdvancedPriceAbsentOnProductForm.php | 50 + .../Test/Constraint/AssertAttributeForm.php | 2 +- .../AssertAttributeOptionsOnProductForm.php | 2 +- .../Constraint/AssertAttributeSetForm.php | 2 +- .../AssertAttributeSetGroupOnProductForm.php | 2 +- .../Constraint/AssertAttributeSetInGrid.php | 2 +- .../AssertAttributeSetNotInGrid.php | 2 +- .../AssertAttributeSetOnProductForm.php | 2 +- ...AssertAttributeSetSuccessDeleteMessage.php | 2 +- .../AssertAttributeSetSuccessSaveMessage.php | 2 +- .../Test/Constraint/AssertCanSaveProduct.php | 46 + .../AssertCategoryAbsenceOnBackend.php | 2 +- .../AssertCategoryAbsenceOnFrontend.php | 2 +- .../Constraint/AssertCategoryBreadcrumbs.php | 2 +- .../AssertCategoryCannotBeDeleted.php | 2 +- .../AssertCategoryForAssignedProducts.php | 2 +- .../Test/Constraint/AssertCategoryForm.php | 10 +- .../AssertCategoryInNavigationMenu.php | 55 + .../Constraint/AssertCategoryIsNotActive.php | 2 +- .../Constraint/AssertCategoryMovedMessage.php | 49 + .../AssertCategoryOnCustomStore.php | 138 + .../AssertCategoryOnCustomWebsite.php | 78 + .../Test/Constraint/AssertCategoryPage.php | 2 +- .../Constraint/AssertCategoryRedirect.php | 2 +- .../Constraint/AssertCategorySaveMessage.php | 2 +- .../AssertCategorySuccessDeleteMessage.php | 2 +- ...ssertCategoryWithCustomStoreOnFrontend.php | 2 +- .../AssertImagesAreVisibleOnProductPage.php | 2 +- .../AssertMassProductUpdateSuccessMessage.php | 9 +- .../AssertPriceOnProductPageInterface.php | 2 +- .../AssertProductAbsentCrossSells.php | 2 +- .../AssertProductAbsentRelatedProducts.php | 2 +- .../Constraint/AssertProductAbsentUpSells.php | 2 +- .../AssertProductAttributeAbsenceInGrid.php | 2 +- ...tAttributeAbsenceInSearchOnProductForm.php | 2 +- ...roductAttributeAbsenceInTemplateGroups.php | 2 +- ...AttributeAbsenceInUnassignedAttributes.php | 2 +- ...rtProductAttributeDisplayingOnFrontend.php | 2 +- ...ProductAttributeDisplayingOnSearchForm.php | 2 +- .../AssertProductAttributeInGrid.php | 2 +- .../AssertProductAttributeIsComparable.php | 2 +- .../AssertProductAttributeIsFilterable.php | 2 +- ...rtProductAttributeIsFilterableInSearch.php | 2 +- .../AssertProductAttributeIsGlobal.php | 2 +- .../AssertProductAttributeIsHtmlAllowed.php | 2 +- .../AssertProductAttributeIsRequired.php | 2 +- .../AssertProductAttributeIsUnique.php | 2 +- ...ProductAttributeIsUsedInSortOnFrontend.php | 2 +- .../AssertProductAttributeSaveMessage.php | 2 +- ...rtProductAttributeSuccessDeleteMessage.php | 2 +- ...ProductAutoincrementedSkuNoticeMessage.php | 2 +- .../AssertProductCompareBlockOnCmsPage.php | 2 +- .../AssertProductCompareItemsLink.php | 2 +- .../AssertProductCompareItemsLinkIsAbsent.php | 2 +- .../Constraint/AssertProductComparePage.php | 2 +- ...ProductCompareRemoveLastProductMessage.php | 2 +- .../AssertProductCompareSuccessAddMessage.php | 2 +- ...CompareSuccessRemoveAllProductsMessage.php | 2 +- ...sertProductCompareSuccessRemoveMessage.php | 2 +- .../Constraint/AssertProductCrossSells.php | 2 +- ...ssertProductCustomOptionsOnProductPage.php | 2 +- .../Constraint/AssertProductDuplicateForm.php | 2 +- ...ductDuplicateIsNotDisplayingOnFrontend.php | 2 +- .../AssertProductDuplicateMessage.php | 2 +- .../AssertProductDuplicatedInGrid.php | 2 +- .../Test/Constraint/AssertProductForm.php | 2 +- .../AssertProductHasImageInGrid.php | 45 + .../Test/Constraint/AssertProductInCart.php | 2 +- .../Constraint/AssertProductInCategory.php | 2 +- ...AssertProductInCategoryOnCustomWebsite.php | 63 + .../AssertProductInCustomStoreView.php | 116 + .../Test/Constraint/AssertProductInGrid.php | 2 +- .../Test/Constraint/AssertProductInStock.php | 2 +- .../AssertProductInventoryMaxAllowedQty.php | 72 + .../AssertProductInventoryMinAllowedQty.php | 72 + .../AssertProductInventoryThreshold.php | 96 + ...AssertProductIsNotDisplayingOnFrontend.php | 2 +- ...ssertProductIsNotVisibleInCompareBlock.php | 2 +- ...AssertProductIsNotVisibleInComparePage.php | 2 +- ...AssertProductNameOnDifferentStoreViews.php | 60 + .../Constraint/AssertProductNoImageInGrid.php | 2 +- .../Constraint/AssertProductNotInGrid.php | 2 +- .../AssertProductNotSearchableBySku.php | 2 +- .../AssertProductNotVisibleInCategory.php | 2 +- .../AssertProductOnCustomWebsite.php | 2 +- .../Constraint/AssertProductOutOfStock.php | 10 +- .../Test/Constraint/AssertProductPage.php | 8 +- ...ssertProductPriceOnDifferentStoreViews.php | 58 + .../AssertProductRelatedProducts.php | 2 +- .../Constraint/AssertProductSaveMessage.php | 6 +- .../AssertProductSearchableBySku.php | 2 +- .../AssertProductSimpleDuplicateForm.php | 2 +- .../AssertProductSkuAutoGenerated.php | 2 +- ...AssertProductSpecialPriceOnProductPage.php | 2 +- .../AssertProductSuccessDeleteMessage.php | 2 +- .../AssertProductTierPriceInCart.php | 129 + .../AssertProductTierPriceOnProductPage.php | 2 +- ...ductTierPriceOnProductPageWithCustomer.php | 2 +- .../AssertProductTypeOrderOnCreate.php | 2 +- .../Test/Constraint/AssertProductUpSells.php | 2 +- .../Test/Constraint/AssertProductView.php | 2 +- .../AssertProductVisibleInCategory.php | 2 +- .../Test/Constraint/AssertProductsInStock.php | 47 + .../Constraint/AssertProductsOutOfStock.php | 2 +- ...tProductsQtyAndStockStatusInAdminPanel.php | 63 + ...SuperAttributeImpossibleDeleteMessages.php | 2 +- .../Catalog/Test/Fixture/Cart/Item.php | 36 +- .../Test/Fixture/CatalogAttributeSet.xml | 2 +- .../AssignedAttributes.php | 2 +- .../CatalogAttributeSet/SkeletonSet.php | 2 +- .../Test/Fixture/CatalogProductAttribute.xml | 2 +- .../Test/Fixture/CatalogProductSimple.xml | 19 +- .../CatalogProductSimple/CustomAttribute.php | 2 +- .../Test/Fixture/CatalogProductVirtual.xml | 2 +- .../Magento/Catalog/Test/Fixture/Category.xml | 5 +- .../Fixture/Category/CategoryProducts.php | 22 +- .../Test/Fixture/Category/LandingPage.php | 2 +- .../Test/Fixture/Category/ParentId.php | 2 +- .../Catalog/Test/Fixture/Category/StoreId.php | 2 +- .../Test/Fixture/Product/AttributeSetId.php | 2 +- .../Test/Fixture/Product/CategoryIds.php | 18 +- .../Test/Fixture/Product/CustomOptions.php | 2 +- .../Catalog/Test/Fixture/Product/Image.php | 60 + .../Catalog/Test/Fixture/Product/Price.php | 2 +- .../Test/Fixture/Product/RelatedProducts.php | 2 +- .../Catalog/Test/Fixture/Product/TaxClass.php | 2 +- .../Test/Fixture/Product/TierPrice.php | 2 +- .../Test/Fixture/Product/WebsiteIds.php | 57 +- .../CatalogAttributeSetInterface.php | 2 +- .../Test/Handler/CatalogAttributeSet/Curl.php | 2 +- .../CatalogProductAttributeInterface.php | 2 +- .../Handler/CatalogProductAttribute/Curl.php | 43 +- .../CatalogProductSimpleInterface.php | 2 +- .../Handler/CatalogProductSimple/Curl.php | 5 +- .../Test/Handler/CatalogProductSimple/Ui.php | 2 +- .../Handler/CatalogProductSimple/Webapi.php | 31 +- .../CatalogProductVirtualInterface.php | 2 +- .../Handler/CatalogProductVirtual/Curl.php | 2 +- .../Handler/CatalogProductVirtual/Webapi.php | 2 +- .../Handler/Category/CategoryInterface.php | 2 +- .../Catalog/Test/Handler/Category/Curl.php | 2 +- .../Catalog/Test/Handler/Category/Webapi.php | 2 +- .../Page/Adminhtml/CatalogCategoryEdit.xml | 4 +- .../Page/Adminhtml/CatalogCategoryIndex.xml | 2 +- .../CatalogProductActionAttributeEdit.xml | 4 +- .../CatalogProductAttributeIndex.xml | 2 +- .../Adminhtml/CatalogProductAttributeNew.xml | 2 +- .../Page/Adminhtml/CatalogProductEdit.xml | 2 +- .../Page/Adminhtml/CatalogProductIndex.xml | 2 +- .../Test/Page/Adminhtml/CatalogProductNew.xml | 2 +- .../Page/Adminhtml/CatalogProductSetAdd.xml | 2 +- .../Page/Adminhtml/CatalogProductSetEdit.xml | 2 +- .../Page/Adminhtml/CatalogProductSetIndex.xml | 2 +- .../Test/Page/Category/CatalogCategory.php | 2 +- .../Page/Category/CatalogCategoryEdit.php | 2 +- .../Page/Category/CatalogCategoryView.xml | 3 +- .../Magento/Catalog/Test/Page/CmsIndex.xml | 2 +- .../Page/Product/CatalogProductCompare.xml | 2 +- .../Test/Page/Product/CatalogProductView.xml | 2 +- .../Test/Repository/CatalogAttributeSet.xml | 42 +- .../Repository/CatalogProductAttribute.xml | 106 +- .../CatalogProductAttribute/Options.xml | 2 +- .../Test/Repository/CatalogProductSimple.xml | 745 +- .../CatalogProductSimple/CheckoutData.xml | 119 +- .../Repository/CatalogProductSimple/Price.xml | 2 +- .../CatalogProductSimple/WebsiteData.xml | 19 + .../Test/Repository/CatalogProductVirtual.xml | 62 +- .../CatalogProductVirtual/CheckoutData.xml | 7 +- .../Catalog/Test/Repository/Category.xml | 68 +- .../Catalog/Test/Repository/ConfigData.xml | 96 +- .../Test/Repository/Product/CustomOptions.xml | 134 +- .../Catalog/Test/Repository/Product/Fpt.xml | 2 +- .../Test/Repository/Product/TierPrice.xml | 37 +- .../Category/CreateCategoryEntityTest.php | 4 +- .../Category/CreateCategoryEntityTest.xml | 29 +- .../Category/DeleteCategoryEntityTest.php | 2 +- .../Category/DeleteCategoryEntityTest.xml | 2 +- .../Category/MoveCategoryEntityTest.php | 92 + .../Category/MoveCategoryEntityTest.xml | 18 + .../UpdateCategoryEntityFlatDataTest.php | 131 + .../UpdateCategoryEntityFlatDataTest.xml | 95 + .../Category/UpdateCategoryEntityTest.php | 17 +- .../Category/UpdateCategoryEntityTest.xml | 19 +- .../Category/UpdateTopCategoryEntityTest.php | 179 + .../Category/UpdateTopCategoryEntityTest.xml | 27 + .../Test/TestCase/NavigateMenuTest.xml | 4 +- .../Product/AbstractCompareProductsTest.php | 2 +- .../AbstractProductPromotedProductsTest.php | 2 +- .../Product/AddCompareProductsTest.php | 3 +- .../Product/AddCompareProductsTest.xml | 3 +- .../Product/AddToCartCrossSellTest.php | 2 +- .../Product/AddToCartCrossSellTest.xml | 4 +- .../Product/ClearAllCompareProductsTest.php | 2 +- .../Product/ClearAllCompareProductsTest.xml | 3 +- ...pleProductEntityByAttributeMaskSkuTest.php | 134 + ...pleProductEntityByAttributeMaskSkuTest.xml | 23 + .../Product/CreateSimpleProductEntityTest.php | 4 +- .../Product/CreateSimpleProductEntityTest.xml | 135 +- .../CreateVirtualProductEntityTest.php | 2 +- .../CreateVirtualProductEntityTest.xml | 8 +- .../Product/DeleteCompareProductsTest.php | 3 +- .../Product/DeleteCompareProductsTest.xml | 4 +- .../Product/DeleteProductEntityTest.php | 2 +- .../Product/DeleteProductEntityTest.xml | 3 +- .../Product/DuplicateProductEntityTest.php | 2 +- .../Product/DuplicateProductEntityTest.xml | 3 +- .../Product/ManageProductsStockTest.php | 101 + .../Product/ManageProductsStockTest.xml | 72 + .../Product/MassProductUpdateTest.php | 72 +- .../Product/MassProductUpdateTest.xml | 7 +- .../Product/NavigateRelatedProductsTest.php | 2 +- .../Product/NavigateRelatedProductsTest.xml | 4 +- .../Product/NavigateUpSellProductsTest.php | 2 +- .../Product/NavigateUpSellProductsTest.xml | 4 +- .../ProductTypeSwitchingOnCreationTest.php | 2 +- .../ProductTypeSwitchingOnCreationTest.xml | 6 +- .../ProductTypeSwitchingOnUpdateTest.php | 2 +- .../ProductTypeSwitchingOnUpdateTest.xml | 13 +- .../Product/UpdateSimpleProductEntityTest.php | 65 +- .../Product/UpdateSimpleProductEntityTest.xml | 47 +- .../UpdateVirtualProductEntityTest.php | 3 +- .../UpdateVirtualProductEntityTest.xml | 2 +- .../ValidateOrderOfProductTypeTest.php | 2 +- .../ValidateOrderOfProductTypeTest.xml | 2 +- .../CreateAttributeSetEntityTest.php | 2 +- .../CreateAttributeSetEntityTest.xml | 3 +- ...ductAttributeEntityFromProductPageTest.php | 2 +- ...ductAttributeEntityFromProductPageTest.xml | 2 +- .../CreateProductAttributeEntityTest.php | 2 +- .../CreateProductAttributeEntityTest.xml | 19 +- ...AssignedToTemplateProductAttributeTest.php | 2 +- ...AssignedToTemplateProductAttributeTest.xml | 2 +- .../DeleteAttributeSetTest.php | 2 +- .../DeleteAttributeSetTest.xml | 3 +- .../DeleteProductAttributeEntityTest.php | 2 +- .../DeleteProductAttributeEntityTest.xml | 3 +- .../DeleteSystemProductAttributeTest.php | 2 +- .../DeleteSystemProductAttributeTest.xml | 2 +- ...UsedInConfigurableProductAttributeTest.php | 2 +- ...UsedInConfigurableProductAttributeTest.xml | 2 +- .../UpdateAttributeSetTest.php | 2 +- .../UpdateAttributeSetTest.xml | 3 +- .../UpdateProductAttributeEntityTest.php | 4 +- .../UpdateProductAttributeEntityTest.xml | 22 +- .../AddAttributeToAttributeSetStep.php | 2 +- .../AddNewAttributeFromProductPageStep.php | 2 +- .../Test/TestStep/AddNewAttributeStep.php | 2 +- .../ConfigureProductOnProductPageStep.php | 66 + .../Test/TestStep/CreateAttributeSetStep.php | 2 +- .../Test/TestStep/CreateProductStep.php | 2 +- .../CreateProductWithAttributeSetStep.php | 2 +- .../Test/TestStep/CreateProductsStep.php | 24 +- .../Test/TestStep/DeleteAttributeStep.php | 2 +- .../FillAttributeFormOnProductPageStep.php | 2 +- .../Test/TestStep/FillAttributeFormStep.php | 2 +- .../MergePreconditionProductsStep.php | 51 + .../OpenProductAttributesPageStep.php | 2 +- .../TestStep/OpenProductOnBackendStep.php | 2 +- .../TestStep/OpenProductsOnFrontendStep.php | 2 +- .../SaveAttributeOnProductPageStep.php | 2 +- .../Test/TestStep/SaveAttributeSetStep.php | 2 +- .../Test/TestStep/SaveAttributeStep.php | 2 +- .../Catalog/Test/TestStep/SaveProductStep.php | 2 +- .../TestStep/SetDefaultAttributeValueStep.php | 2 +- .../app/Magento/Catalog/Test/_files/test1.png | Bin 0 -> 3716 bytes .../app/Magento/Catalog/Test/_files/test2.png | Bin 0 -> 4115 bytes .../app/Magento/Catalog/Test/etc/curl/di.xml | 2 +- .../tests/app/Magento/Catalog/Test/etc/di.xml | 9 +- .../app/Magento/Catalog/Test/etc/testcase.xml | 2 +- .../app/Magento/Catalog/Test/etc/ui/di.xml | 2 +- .../Magento/Catalog/Test/etc/webapi/di.xml | 2 +- .../Test/Repository/ConfigData.xml | 20 +- ...tOutOfStockOptionIsAbsentOnProductPage.php | 72 + .../ConfigurableAttributesData.xml | 44 + .../CreateConfigurableProductEntityTest.xml | 25 + .../Test/etc/di.xml | 14 + .../Test/Block/Adminhtml/FormPageActions.php | 2 +- .../Test/Block/Adminhtml/Promo/Catalog.php | 2 +- .../Promo/Catalog/Edit/PromoForm.php | 2 +- .../Promo/Catalog/Edit/PromoForm.xml | 2 +- .../Promo/Catalog/Edit/Section/Conditions.php | 2 +- .../Catalog/Edit/Section/RuleInformation.php | 2 +- .../Block/Adminhtml/Promo/GridPageActions.php | 2 +- .../CatalogRule/Test/Block/Conditions.php | 2 +- ...sertCatalogPriceRuleAppliedCatalogPage.php | 8 +- ...sertCatalogPriceRuleAppliedProductPage.php | 8 +- ...ertCatalogPriceRuleAppliedShoppingCart.php | 2 +- .../Constraint/AssertCatalogPriceRuleForm.php | 2 +- .../AssertCatalogPriceRuleInGrid.php | 2 +- ...tCatalogPriceRuleNotAppliedCatalogPage.php | 2 +- ...tCatalogPriceRuleNotAppliedProductPage.php | 2 +- ...CatalogPriceRuleNotAppliedShoppingCart.php | 2 +- .../AssertCatalogPriceRuleNotInGrid.php | 2 +- .../AssertCatalogPriceRuleNoticeMessage.php | 2 +- ...sertCatalogPriceRuleOnOnepageCheckout.php} | 4 +- ...rtCatalogPriceRuleSuccessDeleteMessage.php | 2 +- ...sertCatalogPriceRuleSuccessSaveMessage.php | 2 +- ...AssertProductAttributeIsUsedPromoRules.php | 2 +- .../CatalogRule/Test/Fixture/CatalogRule.xml | 2 +- .../CatalogRule/CatalogRuleInterface.php | 2 +- .../Test/Handler/CatalogRule/Curl.php | 2 +- .../Test/Page/Adminhtml/CatalogRuleEdit.xml | 2 +- .../Test/Page/Adminhtml/CatalogRuleIndex.xml | 2 +- .../Test/Page/Adminhtml/CatalogRuleNew.xml | 2 +- .../Test/Repository/CatalogRule.xml | 2 +- .../AbstractCatalogRuleEntityTest.php | 2 +- .../TestCase/ApplyCatalogPriceRulesTest.php | 123 +- .../TestCase/ApplyCatalogPriceRulesTest.xml | 96 +- .../CreateCatalogPriceRuleEntityTest.php | 2 +- .../CreateCatalogPriceRuleEntityTest.xml | 2 +- .../DeleteCatalogPriceRuleEntityTest.php | 15 +- .../DeleteCatalogPriceRuleEntityTest.xml | 13 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../UpdateCatalogPriceRuleEntityTest.php | 2 +- .../UpdateCatalogPriceRuleEntityTest.xml | 4 +- .../Test/TestStep/CreateCatalogRuleStep.php | 2 +- .../TestStep/DeleteAllCatalogRulesStep.php | 2 +- .../Magento/CatalogRule/Test/etc/curl/di.xml | 2 +- .../app/Magento/CatalogRule/Test/etc/di.xml | 2 +- .../Magento/CatalogRule/Test/etc/ui/di.xml | 2 +- ...nfigurableProductCatalogPriceRulesTest.php | 50 + ...nfigurableProductCatalogPriceRulesTest.xml | 76 + .../DeleteCatalogPriceRuleEntityTest.xml | 2 +- .../Block/Adminhtml/Edit/SearchTermForm.php | 2 +- .../Block/Adminhtml/Edit/SearchTermForm.xml | 2 +- .../Test/Block/Adminhtml/Grid.php | 2 +- .../Block/Advanced/CustomAttribute/Date.php | 2 +- .../Block/Advanced/CustomAttribute/Select.php | 2 +- .../Block/Advanced/CustomAttribute/Text.php | 2 +- .../Test/Block/Advanced/Form.php | 13 +- .../Test/Block/Advanced/Form.xml | 8 +- .../Test/Block/Advanced/Result.php | 2 +- .../Block/Advanced/SearchResultsTitle.php | 37 + .../AssertAdvancedSearchAttributeIsAbsent.php | 50 + .../AssertAdvancedSearchEmptyTerm.php | 49 + .../AssertAdvancedSearchNoResult.php | 2 +- ...AssertAdvancedSearchProductByAttribute.php | 19 +- .../AssertAdvancedSearchProductResult.php | 103 + .../AssertAdvancedSearchProductsResult.php | 8 +- .../AssertAttributeSearchableByLabel.php | 7 +- .../AssertCatalogSearchNoResult.php | 2 +- .../AssertCatalogSearchNoResultMessage.php | 2 +- .../AssertCatalogSearchQueryLength.php | 41 + .../Constraint/AssertCatalogSearchResult.php | 10 +- .../AssertCatalogSearchResultOrder.php | 75 + ...ertProductAddedToCartFromSearchResults.php | 70 + ...sertProductCanBeOpenedFromSearchResult.php | 9 +- .../Constraint/AssertSearchAttributeTest.php | 52 + .../Test/Constraint/AssertSearchTermForm.php | 2 +- .../Constraint/AssertSearchTermInGrid.php | 2 +- ...ssertSearchTermMassActionNotOnFrontend.php | 2 +- .../AssertSearchTermMassActionsNotInGrid.php | 2 +- .../Constraint/AssertSearchTermNotInGrid.php | 2 +- .../AssertSearchTermNotOnFrontend.php | 2 +- .../Constraint/AssertSearchTermOnFrontend.php | 2 +- .../AssertSearchTermSuccessDeleteMessage.php | 2 +- ...sertSearchTermSuccessMassDeleteMessage.php | 2 +- .../AssertSearchTermSuccessSaveMessage.php | 2 +- .../AssertSuggestSearchingResult.php | 2 +- .../Test/Fixture/CatalogSearchQuery.xml | 2 +- .../Fixture/CatalogSearchQuery/QueryText.php | 109 +- .../CatalogSearchQueryInterface.php | 2 +- .../Test/Handler/CatalogSearchQuery/Curl.php | 2 +- .../Test/Page/Adminhtml/CatalogSearchEdit.xml | 2 +- .../Page/Adminhtml/CatalogSearchIndex.xml | 2 +- .../Test/Page/AdvancedResult.xml | 3 +- .../Test/Page/AdvancedSearch.xml | 3 +- .../Test/Page/CatalogsearchResult.xml | 3 +- .../Test/Repository/CatalogSearchQuery.xml | 2 +- .../TestCase/AdvancedSearchEntityTest.php | 4 +- .../TestCase/AdvancedSearchEntityTest.xml | 7 +- .../TestCase/CreateSearchTermEntityTest.php | 2 +- .../TestCase/CreateSearchTermEntityTest.xml | 2 +- .../TestCase/DeleteSearchTermEntityTest.php | 2 +- .../TestCase/DeleteSearchTermEntityTest.xml | 2 +- .../MassDeleteSearchTermEntityTest.php | 2 +- .../MassDeleteSearchTermEntityTest.xml | 2 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../Test/TestCase/SearchEntityResultsTest.php | 9 +- .../Test/TestCase/SearchEntityResultsTest.xml | 79 +- .../SuggestSearchingResultEntityTest.php | 2 +- .../SuggestSearchingResultEntityTest.xml | 4 +- .../TestCase/UpdateSearchTermEntityTest.php | 2 +- .../TestCase/UpdateSearchTermEntityTest.xml | 2 +- .../CatalogSearch/Test/etc/curl/di.xml | 2 +- .../app/Magento/CatalogSearch/Test/etc/di.xml | 2 +- .../Adminhtml/Category/Edit/CategoryForm.xml | 17 + .../Test/Fixture/Category.xml | 12 + .../app/Magento/Checkout/Test/Block/Cart.php | 11 +- .../Test/Block/Cart/AbstractCartItem.php | 2 +- .../Checkout/Test/Block/Cart/CartEmpty.php | 2 +- .../Checkout/Test/Block/Cart/CartItem.php | 2 +- .../Test/Block/Cart/DiscountCodes.php | 2 +- .../Checkout/Test/Block/Cart/Pager.php | 50 + .../Checkout/Test/Block/Cart/Shipping.php | 57 +- .../Checkout/Test/Block/Cart/Shipping.xml | 2 +- .../Checkout/Test/Block/Cart/Sidebar.php | 50 +- .../Checkout/Test/Block/Cart/Sidebar/Item.php | 4 +- .../Checkout/Test/Block/Cart/Totals.php | 6 +- .../Test/Block/Onepage/AbstractReview.php | 69 +- .../Test/Block/Onepage/CustomAddress.php | 51 + .../Checkout/Test/Block/Onepage/Link.php | 2 +- .../Checkout/Test/Block/Onepage/Login.php | 17 +- .../Checkout/Test/Block/Onepage/Login.xml | 2 +- .../Checkout/Test/Block/Onepage/Payment.php | 56 +- .../Block/Onepage/Payment/DiscountCodes.php | 2 +- .../Test/Block/Onepage/Payment/Method.php | 22 +- .../Block/Onepage/Payment/Method/Billing.php | 20 +- .../Block/Onepage/Payment/Method/Billing.xml | 2 +- .../Test/Block/Onepage/Registration.php | 2 +- .../Checkout/Test/Block/Onepage/Review.php | 2 +- .../Checkout/Test/Block/Onepage/Shipping.php | 210 +- .../Checkout/Test/Block/Onepage/Shipping.xml | 2 +- .../Block/Onepage/Shipping/AddressModal.php | 84 + .../Block/Onepage/Shipping/AddressModal.xml | 29 + .../Test/Block/Onepage/Shipping/Method.php | 73 +- .../Test/Block/Onepage/ShippingPopup.php | 32 + .../Test/Block/Onepage/ShippingPopup.xml | 26 + .../Checkout/Test/Block/Onepage/Success.php | 2 +- ...ertAddToCartButtonAbsentOnCategoryPage.php | 61 + ...sertAddToCartButtonAbsentOnProductPage.php | 48 + ...rtAddToCartButtonPresentOnCategoryPage.php | 60 + ...ertAddToCartButtonPresentOnProductPage.php | 48 + ...AssertAddedProductToCartSuccessMessage.php | 2 +- .../AssertBillingAddressAbsentInPayment.php | 43 + ...rtBillingAddressSameAsShippingCheckbox.php | 2 +- ...sertCancelSuccessMessageInShoppingCart.php | 47 + .../Test/Constraint/AssertCartIsEmpty.php | 2 +- .../Constraint/AssertCartItemsOptions.php | 2 +- .../Test/Constraint/AssertCartPerCustomer.php | 77 + .../Constraint/AssertCheckoutErrorMessage.php | 42 + ...CustomerIsRedirectedToCheckoutFromCart.php | 115 + .../AssertDiscountInShoppingCart.php | 2 +- .../AssertEmailErrorValidationMessage.php | 47 + .../Test/Constraint/AssertEmailToolTips.php | 58 + .../AssertEstimateShippingAndTax.php | 2 +- .../AssertGrandTotalInShoppingCart.php | 2 +- .../AssertGrandTotalOrderReview.php | 10 +- .../AssertItemsCounterInMiniShoppingCart.php | 45 + ...rtLinkGoToCartNotPresentInSummaryBlock.php | 39 + ...ssertLinkGoToCartPresentInSummaryBlock.php | 39 + .../Test/Constraint/AssertMinicartEmpty.php | 2 +- .../Constraint/AssertMinicartItemsQty.php | 45 + .../Constraint/AssertMyCartLinkRedirect.php | 48 + .../AssertOrderSuccessPlacedMessage.php | 2 +- .../AssertPagersNotPresentInShoppingCart.php | 43 + .../AssertPagersPresentInShoppingCart.php | 42 + .../Constraint/AssertPagersSummaryText.php | 54 + ...sertPaymentMethodIsAbsentOnPaymentPage.php | 41 + .../Constraint/AssertPriceInShoppingCart.php | 2 +- .../AssertProductAbsentInMiniShoppingCart.php | 2 +- .../AssertProductDataInMiniShoppingCart.php | 6 +- .../Constraint/AssertProductIsNotEditable.php | 2 +- ...sertProductOptionsAbsentInShoppingCart.php | 2 +- ...AssertProductPresentInMiniShoppingCart.php | 2 +- .../AssertProductPresentInShoppingCart.php | 2 +- .../AssertProductQtyInShoppingCart.php | 2 +- .../AssertProductsAbsentInShoppingCart.php | 2 +- ...ingAddressJsValidationMessagesIsAbsent.php | 47 + .../AssertShippingInShoppingCart.php | 2 +- .../AssertShippingTotalOrderReview.php | 2 +- .../Constraint/AssertSubTotalOrderReview.php | 2 +- .../AssertSubtotalInMiniShoppingCart.php | 2 +- .../AssertSubtotalInShoppingCart.php | 2 +- .../Constraint/AssertTaxInShoppingCart.php | 2 +- .../Constraint/AssertTaxTotalOrderReview.php | 2 +- .../AssertTopDestinationsInSelect.php | 43 + ...tVisibleItemsQtyInCheckoutSummaryBlock.php | 60 + ...ssertVisibleItemsQtyInMiniShoppingCart.php | 59 + ...sibleItemsQtyMessageInMiniShoppingCart.php | 63 + ...eItemsQtyMessageOnCheckoutSummaryBlock.php | 74 + .../Magento/Checkout/Test/Fixture/Cart.xml | 3 +- .../Checkout/Test/Fixture/Cart/Items.php | 27 +- .../Checkout/Test/Page/CheckoutCart.xml | 4 +- .../Checkout/Test/Page/CheckoutOnepage.xml | 8 +- .../Test/Page/CheckoutOnepageSuccess.xml | 2 +- .../Magento/Checkout/Test/Page/CmsIndex.xml | 2 +- .../Checkout/Test/Repository/ConfigData.xml | 126 + .../AddProductsToShoppingCartEntityTest.php | 140 +- .../AddProductsToShoppingCartEntityTest.xml | 153 +- .../DeleteProductFromMiniShoppingCartTest.php | 3 +- .../DeleteProductFromMiniShoppingCartTest.xml | 23 +- .../DeleteProductsFromShoppingCartTest.php | 3 +- .../DeleteProductsFromShoppingCartTest.xml | 10 +- .../TestCase/OnePageCheckoutDeclinedTest.php | 46 + ...nePageCheckoutFromMiniShoppingCartTest.php | 51 + .../OnePageCheckoutJsValidationTest.php | 37 + .../OnePageCheckoutJsValidationTest.xml | 17 + .../Test/TestCase/OnePageCheckoutTest.php | 5 +- .../Test/TestCase/OnePageCheckoutTest.xml | 157 +- .../Test/TestCase/ShoppingCartPagerTest.php | 60 + .../Test/TestCase/ShoppingCartPagerTest.xml | 34 + .../TestCase/ShoppingCartPerCustomerTest.php | 169 + .../TestCase/ShoppingCartPerCustomerTest.xml | 38 + ...eProductFromMiniShoppingCartEntityTest.php | 39 +- ...eProductFromMiniShoppingCartEntityTest.xml | 28 +- .../Test/TestCase/UpdateShoppingCartTest.php | 4 +- .../Test/TestCase/UpdateShoppingCartTest.xml | 4 +- .../TestCase/ValidateEmailOnCheckoutTest.php | 74 + .../TestCase/ValidateEmailOnCheckoutTest.xml | 27 + .../VerifyPaymentMethodOnCheckoutTest.php | 46 + .../TestStep/AddNewShippingAddressStep.php | 57 + .../TestStep/AddProductsToTheCartStep.php | 50 +- .../TestStep/ClickPlaceOrderButtonStep.php | 41 + .../TestStep/ClickProceedToCheckoutStep.php | 2 +- .../TestStep/CreateCustomerAccountStep.php | 2 +- .../TestStep/EstimateShippingAndTaxStep.php | 2 +- .../TestStep/FillBillingInformationStep.php | 57 +- .../Test/TestStep/FillShippingAddressStep.php | 89 +- .../Test/TestStep/FillShippingMethodStep.php | 2 +- .../Test/TestStep/GetPlacedOrderIdStep.php | 74 + .../Checkout/Test/TestStep/PlaceOrderStep.php | 46 +- ...ceedToCheckoutFromMiniShoppingCartStep.php | 42 + .../Test/TestStep/ProceedToCheckoutStep.php | 2 +- .../RemoveProductsFromTheCartStep.php | 83 + .../TestStep/SelectCheckoutMethodStep.php | 48 +- .../Test/TestStep/SelectPaymentMethodStep.php | 43 +- .../app/Magento/Checkout/Test/etc/di.xml | 170 +- .../Magento/Checkout/Test/etc/testcase.xml | 53 +- .../Test/Block/Adminhtml/AgreementGrid.php | 2 +- .../Block/Agreement/Edit/AgreementsForm.php | 2 +- .../Block/Agreement/Edit/AgreementsForm.xml | 2 +- .../MultishippingAgreementReview.php | 2 +- .../Test/Block/Onepage/AgreementReview.php | 2 +- .../Constraint/AssertTermAbsentInGrid.php | 2 +- .../Constraint/AssertTermAbsentOnCheckout.php | 2 +- .../Test/Constraint/AssertTermInGrid.php | 2 +- .../Test/Constraint/AssertTermOnCheckout.php | 2 +- ...mRequireMessageOnMultishippingCheckout.php | 2 +- .../AssertTermSuccessDeleteMessage.php | 2 +- .../AssertTermSuccessSaveMessage.php | 2 +- .../Test/Fixture/CheckoutAgreement.xml | 2 +- .../Test/Fixture/CheckoutAgreement/Stores.php | 2 +- .../CheckoutAgreementInterface.php | 2 +- .../Test/Handler/CheckoutAgreement/Curl.php | 2 +- .../Page/Adminhtml/CheckoutAgreementIndex.xml | 4 +- .../Page/Adminhtml/CheckoutAgreementNew.xml | 4 +- .../Test/Page/CheckoutOnepage.xml | 2 +- .../Page/MultishippingCheckoutOverview.xml | 2 +- .../Test/Repository/CheckoutAgreement.xml | 2 +- .../Test/Repository/ConfigData.xml | 2 +- .../Test/TestCase/CreateTermEntityTest.php | 3 +- .../Test/TestCase/CreateTermEntityTest.xml | 8 +- .../Test/TestCase/DeleteTermEntityTest.php | 3 +- .../Test/TestCase/DeleteTermEntityTest.xml | 3 +- .../Test/TestCase/NavigateMenuTest.xml | 3 +- .../Test/TestCase/UpdateTermEntityTest.php | 3 +- .../Test/TestCase/UpdateTermEntityTest.xml | 5 +- .../TestStep/CheckTermOnMultishippingStep.php | 2 +- .../Test/TestStep/CreateTermEntityStep.php | 2 +- .../TestStep/DeleteAllTermsEntityStep.php | 2 +- .../Test/TestStep/DeleteTermEntityStep.php | 2 +- .../Test/TestStep/SetupTermEntityStep.php | 2 +- .../Test/TestStep/UpdateTermEntityStep.php | 2 +- .../CheckoutAgreements/Test/etc/curl/di.xml | 2 +- .../CheckoutAgreements/Test/etc/di.xml | 42 +- .../CheckoutAgreements/Test/etc/testcase.xml | 2 +- .../Test/Block/Adminhtml/Block/CmsGrid.php | 2 +- .../Block/Adminhtml/Block/Edit/BlockForm.php | 2 +- .../Block/Adminhtml/Block/Edit/CmsForm.php | 2 +- .../Block/Adminhtml/Block/Edit/CmsForm.xml | 2 +- .../Block/Adminhtml/Page/Edit/PageForm.php | 31 +- .../Block/Adminhtml/Page/Edit/PageForm.xml | 2 +- .../Block/Adminhtml/Page/Edit/Tab/Content.php | 12 +- .../Cms/Test/Block/Adminhtml/Page/Grid.php | 9 +- .../Block/Adminhtml/Page/Widget/Chooser.php | 2 +- .../Test/Block/Adminhtml/Wysiwyg/Config.php | 2 +- .../app/Magento/Cms/Test/Block/Messages.php | 15 + .../tests/app/Magento/Cms/Test/Block/Page.php | 2 +- .../AssertCmsBlockDeleteMessage.php | 2 +- .../Test/Constraint/AssertCmsBlockInGrid.php | 2 +- .../Constraint/AssertCmsBlockNotInGrid.php | 2 +- .../AssertCmsBlockNotOnCategoryPage.php | 2 +- .../AssertCmsBlockOnCategoryPage.php | 2 +- .../AssertCmsBlockSuccessSaveMessage.php | 2 +- .../Constraint/AssertCmsPageDeleteMessage.php | 2 +- .../AssertCmsPageDisabledOnFrontend.php | 2 +- .../AssertCmsPageDuplicateErrorMessage.php | 2 +- .../Cms/Test/Constraint/AssertCmsPageForm.php | 2 +- .../AssertCmsPageFormSingleStoreMode.php | 40 + .../Test/Constraint/AssertCmsPageInGrid.php | 6 +- .../Constraint/AssertCmsPageNotInGrid.php | 2 +- .../Constraint/AssertCmsPageOnFrontend.php | 52 + .../Test/Constraint/AssertCmsPagePreview.php | 8 +- .../AssertCmsPageSuccessSaveMessage.php | 2 +- .../AssertCmsPagesDisabledOnFrontend.php | 50 + .../Test/Constraint/AssertCmsPagesInGrid.php | 46 + ...rtCmsPagesOnFrontendMultipleStoreViews.php | 57 + .../AssertUrlRewriteCmsPageRedirect.php | 2 +- .../app/Magento/Cms/Test/Fixture/CmsBlock.xml | 2 +- .../Cms/Test/Fixture/CmsBlock/Stores.php | 2 +- .../app/Magento/Cms/Test/Fixture/CmsPage.xml | 4 +- .../Cms/Test/Fixture/CmsPage/Content.php | 2 +- .../Cms/Test/Fixture/CmsPage/StoreId.php | 94 + .../Handler/CmsBlock/CmsBlockInterface.php | 2 +- .../Cms/Test/Handler/CmsBlock/Curl.php | 2 +- .../Test/Handler/CmsPage/CmsPageInterface.php | 2 +- .../Magento/Cms/Test/Handler/CmsPage/Curl.php | 2 +- .../Cms/Test/Page/Adminhtml/CmsBlockEdit.xml | 2 +- .../Cms/Test/Page/Adminhtml/CmsBlockIndex.xml | 2 +- .../Cms/Test/Page/Adminhtml/CmsBlockNew.xml | 2 +- .../Cms/Test/Page/Adminhtml/CmsPageIndex.xml | 2 +- .../Cms/Test/Page/Adminhtml/CmsPageNew.xml | 2 +- .../app/Magento/Cms/Test/Page/CmsIndex.xml | 5 +- .../app/Magento/Cms/Test/Page/CmsPage.xml | 2 +- .../Magento/Cms/Test/Repository/CmsBlock.xml | 2 +- .../Magento/Cms/Test/Repository/CmsPage.xml | 2 +- .../Cms/Test/Repository/CmsPage/Content.xml | 20 +- .../Cms/Test/Repository/ConfigData.xml | 2 +- .../Cms/Test/Repository/UrlRewrite.xml | 2 +- .../TestCase/AbstractCmsBlockEntityTest.php | 2 +- .../Test/TestCase/CmsPageMassActionTest.php | 85 + .../Test/TestCase/CmsPageMassActionTest.xml | 19 + .../TestCase/CreateCmsBlockEntityTest.php | 2 +- .../TestCase/CreateCmsBlockEntityTest.xml | 2 +- ...ateCmsPageEntityMultipleStoreViewsTest.php | 87 + ...ateCmsPageEntityMultipleStoreViewsTest.xml | 36 + .../Test/TestCase/CreateCmsPageEntityTest.php | 35 +- .../Test/TestCase/CreateCmsPageEntityTest.xml | 27 +- .../CreateCmsPageRewriteEntityTest.php | 2 +- .../CreateCmsPageRewriteEntityTest.xml | 2 +- .../CreateCustomUrlRewriteEntityTest.xml | 2 +- .../TestCase/DeleteCmsBlockEntityTest.php | 2 +- .../TestCase/DeleteCmsBlockEntityTest.xml | 4 +- .../Test/TestCase/DeleteCmsPageEntityTest.php | 2 +- .../Test/TestCase/DeleteCmsPageEntityTest.xml | 4 +- .../DeleteCmsPageUrlRewriteEntityTest.php | 2 +- .../DeleteCmsPageUrlRewriteEntityTest.xml | 2 +- .../Cms/Test/TestCase/GridFilteringTest.xml | 6 +- .../Test/TestCase/GridFullTextSearchTest.xml | 6 +- .../Cms/Test/TestCase/GridSortingTest.xml | 6 +- .../Cms/Test/TestCase/NavigateMenuTest.xml | 2 +- .../TestCase/UpdateCmsBlockEntityTest.php | 2 +- .../TestCase/UpdateCmsBlockEntityTest.xml | 2 +- .../Test/TestCase/UpdateCmsPageEntityTest.php | 2 +- .../Test/TestCase/UpdateCmsPageEntityTest.xml | 6 +- .../UpdateCmsPageRewriteEntityTest.php | 2 +- .../UpdateCmsPageRewriteEntityTest.xml | 2 +- .../app/Magento/Cms/Test/etc/curl/di.xml | 2 +- .../tests/app/Magento/Cms/Test/etc/di.xml | 76 +- .../Test/Block/System/Config/AdminForm.php | 2 +- .../Constraint/AssertAdminAccountSharing.php | 2 +- .../Config/Test/Fixture/ConfigData.xml | 2 +- .../Test/Fixture/ConfigData/Section.php | 32 +- .../ConfigData/ConfigDataInterface.php | 2 +- .../Config/Test/Handler/ConfigData/Curl.php | 2 +- .../VerifyAdminAccountSharingEntityTest.php | 5 +- .../VerifyAdminAccountSharingEntityTest.xml | 2 +- .../Test/TestStep/SetupConfigurationStep.php | 41 +- .../app/Magento/Config/Test/etc/curl/di.xml | 2 +- .../Product/AffectedAttributeSet.php | 2 +- .../Product/AffectedAttributeSet.xml | 2 +- .../Product/AssociatedProductGrid.php | 2 +- .../Adminhtml/Product/AttributesGrid.php | 2 +- .../Adminhtml/Product/Composite/Configure.php | 2 +- .../Adminhtml/Product/Composite/Configure.xml | 2 +- .../Edit/NewConfigurableAttributeForm.php | 2 +- .../Edit/NewConfigurableAttributeForm.xml | 2 +- .../Edit/Section/Variations/Config.php | 2 +- .../Section/Variations/Config/Attribute.php | 3 +- .../Section/Variations/Config/Attribute.xml | 2 +- .../Config/Attribute/AttributeSelector.php | 2 +- .../Config/Attribute/ToggleDropdown.php | 2 +- .../Edit/Section/Variations/Config/Matrix.php | 2 +- .../Edit/Section/Variations/Config/Matrix.xml | 2 +- .../Adminhtml/Product/FormPageActions.php | 2 +- .../Test/Block/Adminhtml/Product/Grid.php | 32 + .../Block/Adminhtml/Product/ProductForm.php | 2 +- .../Block/Adminhtml/Product/ProductForm.xml | 2 +- .../Test/Block/Product/View.php | 2 +- .../Product/View/ConfigurableOptions.php | 94 +- ...rtChildProductIsNotDisplayedSeparately.php | 2 +- .../AssertChildProductsGeneratedSku.php | 2 +- .../Constraint/AssertChildProductsInGrid.php | 2 +- ...figurableAttributesAbsentOnProductPage.php | 2 +- ...leAttributesBlockIsAbsentOnProductPage.php | 2 +- ...AssertConfigurableProductDuplicateForm.php | 2 +- .../AssertConfigurableProductForm.php | 2 +- .../AssertConfigurableProductInCart.php | 2 +- .../AssertConfigurableProductInCategory.php | 2 +- ...ProductInCustomerWishlistOnBackendGrid.php | 2 +- .../AssertConfigurableProductPage.php | 22 +- ...ertConfigurableProductsQtyAfterReorder.php | 70 + ...AssertCurrencyRateAppliedOnProductPage.php | 77 + ...ductAttributeAbsenceInVariationsSearch.php | 2 +- .../AssertProductAttributeIsConfigurable.php | 2 +- ...sertProductQtyDecreasedAfterCreditmemo.php | 118 + .../AssertProductTierPriceOnProductPage.php | 47 + .../Test/Fixture/Cart/Item.php | 34 +- .../Test/Fixture/ConfigurableProduct.xml | 2 +- .../ConfigurableAttributesData.php | 13 +- .../ConfigurableProductInterface.php | 2 +- .../Test/Handler/ConfigurableProduct/Curl.php | 4 +- .../Handler/ConfigurableProduct/Webapi.php | 2 +- .../Page/Adminhtml/CatalogProductEdit.xml | 2 +- .../Test/Page/Adminhtml/CatalogProductNew.xml | 2 +- .../Test/Page/Adminhtml/CustomerIndexEdit.xml | 2 +- .../Test/Page/Adminhtml/OrderCreateIndex.xml | 12 +- .../Test/Page/Product/CatalogProductView.xml | 4 +- .../Test/Repository/ConfigurableProduct.xml | 497 +- .../ConfigurableProduct/CheckoutData.xml | 237 +- .../ConfigurableAttributesData.xml | 245 +- .../Repository/ConfigurableProduct/Price.xml | 13 +- .../CreateConfigurableProductEntityTest.php | 2 +- .../CreateConfigurableProductEntityTest.xml | 23 +- .../TestCase/CreateCreditMemoEntityTest.xml | 22 + .../Test/TestCase/CreateCurrencyRateTest.xml | 28 + .../Test/TestCase/DeleteProductEntityTest.xml | 2 +- .../DeleteProductFromMiniShoppingCartTest.xml | 2 +- .../TestCase/DuplicateProductEntityTest.xml | 3 +- .../Test/TestCase/MassProductUpdateTest.xml | 22 + ...ecentlyComparedProductsOnOrderPageTest.xml | 16 + .../Test/TestCase/TaxCalculationTest.xml | 2 +- .../UpdateConfigurableProductEntityTest.php | 3 +- .../UpdateConfigurableProductEntityTest.xml | 2 +- .../ValidateOrderOfProductTypeTest.xml | 2 +- .../UpdateConfigurableProductStep.php | 2 +- .../ConfigurableProduct/Test/etc/curl/di.xml | 2 +- .../ConfigurableProduct/Test/etc/di.xml | 15 +- .../ConfigurableProduct/Test/etc/testcase.xml | 2 +- .../Test/etc/webapi/di.xml | 2 +- .../System/Currency/Rate/CurrencyRateForm.php | 2 +- .../System/Currency/Rate/CurrencyRateForm.xml | 2 +- .../System/Currency/Rate/FormPageActions.php | 2 +- .../Adminhtml/System/CurrencySymbolForm.php | 2 +- .../Adminhtml/System/CurrencySymbolForm.xml | 2 +- .../Adminhtml/System/FormPageActions.php | 2 +- .../AssertCurrencySymbolOnCatalogPage.php | 2 +- .../AssertCurrencySymbolOnProductPage.php | 2 +- ...rrencySymbolOnProductPageCustomWebsite.php | 56 + ...CurrencySymbolOnProductPageMainWebsite.php | 54 + ...AssertCurrencySymbolSuccessSaveMessage.php | 2 +- .../Test/Fixture/CurrencySymbolEntity.xml | 2 +- .../Handler/CurrencySymbolEntity/Curl.php | 2 +- .../CurrencySymbolEntityInterface.php | 2 +- .../Page/Adminhtml/ConfigCurrencySetUp.xml | 2 +- .../Page/Adminhtml/SystemCurrencyIndex.xml | 2 +- .../Adminhtml/SystemCurrencySymbolIndex.xml | 4 +- .../Test/Repository/ConfigData.xml | 93 +- .../Test/Repository/CurrencySymbolEntity.xml | 3 +- .../AbstractCurrencySymbolEntityTest.php | 4 +- .../EditCurrencyCustomWebsiteTest.php | 126 + .../EditCurrencyCustomWebsiteTest.xml | 34 + .../TestCase/EditCurrencySymbolEntityTest.php | 3 +- .../TestCase/EditCurrencySymbolEntityTest.xml | 2 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../ResetCurrencySymbolEntityTest.php | 3 +- .../ResetCurrencySymbolEntityTest.xml | 2 +- .../CurrencySymbol/Test/etc/curl/di.xml | 2 +- .../Block/Account/AddressesAdditional.php | 2 +- .../Test/Block/Account/AddressesDefault.php | 2 +- .../Block/Account/AuthenticationPopup.php | 38 +- .../Block/Account/AuthenticationPopup.xml | 2 +- .../Block/Account/AuthenticationWrapper.php | 32 + .../Block/Account/AuthenticationWrapper.xml | 17 + .../Test/Block/Account/Dashboard/Address.php | 2 +- .../Test/Block/Account/Dashboard/Info.php | 2 +- .../Customer/Test/Block/Account/Links.php | 2 +- .../Customer/Test/Block/Address/Edit.php | 18 +- .../Customer/Test/Block/Address/Edit.xml | 2 +- .../Customer/Test/Block/Address/Renderer.php | 12 +- .../Test/Block/Adminhtml/CustomerGrid.php | 2 +- .../Block/Adminhtml/Edit/CustomerForm.php | 2 +- .../Block/Adminhtml/Edit/CustomerForm.xml | 2 +- .../Block/Adminhtml/Edit/FormPageActions.php | 26 +- .../Block/Adminhtml/Edit/Tab/Addresses.php | 2 +- .../Block/Adminhtml/Edit/Tab/Addresses.xml | 2 +- .../Adminhtml/Group/CustomerGroupGrid.php | 2 +- .../Test/Block/Adminhtml/Group/Edit/Form.php | 2 +- .../Test/Block/Adminhtml/Group/Edit/Form.xml | 2 +- .../Customer/Test/Block/Form/CustomerForm.php | 105 +- .../Customer/Test/Block/Form/CustomerForm.xml | 8 +- .../Test/Block/Form/ForgotPassword.php | 2 +- .../Test/Block/Form/ForgotPassword.xml | 4 +- .../Customer/Test/Block/Form/Login.php | 38 +- .../Customer/Test/Block/Form/Login.xml | 2 +- .../Customer/Test/Block/Form/Register.php | 17 +- .../Customer/Test/Block/Form/Register.xml | 2 +- ...AssertAdditionalAddressDeletedFrontend.php | 2 +- .../AssertAddressDeletedBackend.php | 2 +- .../AssertAddressDeletedFrontend.php | 2 +- .../AssertChangePasswordFailMessage.php | 2 +- .../AssertChangingWebsiteChangeCountries.php | 2 +- ...ssertCustomerAddressSuccessSaveMessage.php | 2 +- .../AssertCustomerBackendBackButton.php | 2 +- ...rtCustomerBackendDuplicateErrorMessage.php | 2 +- .../AssertCustomerBackendFormTitle.php | 2 +- .../AssertCustomerBackendRequiredFields.php | 2 +- ...tomerDefaultAddressFrontendAddressBook.php | 47 +- .../AssertCustomerDefaultAddresses.php | 2 +- .../AssertCustomerFailRegisterMessage.php | 2 +- ...rtCustomerForgotPasswordSuccessMessage.php | 2 +- .../Test/Constraint/AssertCustomerForm.php | 3 +- .../AssertCustomerGroupAlreadyExists.php | 2 +- ...merGroupChangedToDefaultOnCustomerForm.php | 4 +- .../AssertCustomerGroupFieldsDisabled.php | 2 +- .../Constraint/AssertCustomerGroupForm.php | 2 +- .../Constraint/AssertCustomerGroupInGrid.php | 2 +- .../AssertCustomerGroupNotInGrid.php | 2 +- ...ertCustomerGroupNotOnCartPriceRuleForm.php | 2 +- ...CustomerGroupNotOnCatalogPriceRuleForm.php | 2 +- .../AssertCustomerGroupNotOnProductForm.php | 2 +- ...AssertCustomerGroupOnCartPriceRuleForm.php | 2 +- ...ertCustomerGroupOnCatalogPriceRuleForm.php | 2 +- .../AssertCustomerGroupOnCustomerForm.php | 2 +- .../AssertCustomerGroupOnProductForm.php | 2 +- ...ssertCustomerGroupSuccessDeleteMessage.php | 2 +- .../AssertCustomerGroupSuccessSaveMessage.php | 2 +- .../Test/Constraint/AssertCustomerInGrid.php | 2 +- .../AssertCustomerInfoSuccessSavedMessage.php | 2 +- .../Constraint/AssertCustomerInvalidEmail.php | 2 +- .../Test/Constraint/AssertCustomerLogin.php | 2 +- .../AssertCustomerLoginErrorMessage.php | 47 + .../Test/Constraint/AssertCustomerLogout.php | 2 +- .../AssertCustomerMassDeleteInGrid.php | 2 +- .../AssertCustomerMassDeleteNotInGrid.php | 2 +- ...AssertCustomerMassDeleteSuccessMessage.php | 2 +- .../Constraint/AssertCustomerNameFrontend.php | 2 +- .../Constraint/AssertCustomerNotInGrid.php | 2 +- ...sswordAutocompleteOnAuthorizationPopup.php | 47 + ...rtCustomerPasswordAutocompleteOnSignIn.php | 41 + .../AssertCustomerPasswordChanged.php | 2 +- .../AssertCustomerRedirectToDashboard.php | 2 +- .../AssertCustomerSuccessDeleteMessage.php | 2 +- .../AssertCustomerSuccessRegisterMessage.php | 2 +- .../AssertCustomerSuccessSaveMessage.php | 2 +- .../AssertMassActionSuccessUpdateMessage.php | 7 +- .../AssertNoDeleteForSystemCustomerGroup.php | 2 +- .../AssertWrongPassConfirmationMessage.php | 2 +- .../Magento/Customer/Test/Fixture/Address.xml | 2 +- .../Customer/Test/Fixture/Customer.xml | 4 +- .../Test/Fixture/Customer/Address.php | 2 +- .../Test/Fixture/Customer/GroupId.php | 2 +- .../Test/Fixture/Customer/WebsiteId.php | 118 + .../Customer/Test/Fixture/CustomerGroup.xml | 2 +- .../Fixture/CustomerGroup/TaxClassIds.php | 2 +- .../Customer/Test/Handler/Customer/Curl.php | 17 +- .../Handler/Customer/CustomerInterface.php | 2 +- .../Customer/Test/Handler/Customer/Webapi.php | 68 +- .../Test/Handler/CustomerGroup/Curl.php | 2 +- .../CustomerGroup/CustomerGroupInterface.php | 2 +- .../Test/Page/Address/DefaultAddress.php | 2 +- .../Test/Page/Adminhtml/CheckoutIndex.xml | 12 + .../Test/Page/Adminhtml/CustomerGroupEdit.xml | 2 +- .../Page/Adminhtml/CustomerGroupIndex.xml | 2 +- .../Test/Page/Adminhtml/CustomerGroupNew.xml | 2 +- .../Test/Page/Adminhtml/CustomerIndex.xml | 2 +- .../Test/Page/Adminhtml/CustomerIndexEdit.xml | 2 +- .../Test/Page/Adminhtml/CustomerIndexNew.xml | 2 +- .../Test/Page/CustomerAccountAddress.xml | 4 +- .../Test/Page/CustomerAccountCreate.xml | 2 +- .../Test/Page/CustomerAccountEdit.xml | 3 +- .../Page/CustomerAccountForgotPassword.xml | 2 +- .../Test/Page/CustomerAccountIndex.xml | 2 +- .../Test/Page/CustomerAccountLogin.xml | 10 +- .../Test/Page/CustomerAccountLogout.php | 2 +- .../Test/Page/CustomerAddressEdit.php | 51 - .../Test/Page/CustomerAddressEdit.xml | 12 + .../Customer/Test/Repository/Address.php | 2 +- .../Customer/Test/Repository/Address.xml | 112 +- .../Customer/Test/Repository/ConfigData.xml | 11 +- .../Customer/Test/Repository/Customer.xml | 120 +- .../Test/Repository/CustomerGroup.xml | 2 +- .../Test/TestCase/AbstractApplyVatIdTest.php | 2 +- .../Customer/Test/TestCase/ApplyVatIdTest.php | 2 +- .../Customer/Test/TestCase/ApplyVatIdTest.xml | 2 +- .../TestCase/ChangeCustomerPasswordTest.php | 2 +- .../TestCase/ChangeCustomerPasswordTest.xml | 2 +- .../CreateCustomerBackendEntityTest.php | 2 +- .../CreateCustomerBackendEntityTest.xml | 5 +- .../CreateCustomerGroupEntityTest.php | 3 +- .../CreateCustomerGroupEntityTest.xml | 2 +- .../CreateExistingCustomerBackendEntity.php | 2 +- .../CreateExistingCustomerBackendEntity.xml | 2 +- .../CreateExistingCustomerFrontendEntity.php | 2 +- .../CreateExistingCustomerFrontendEntity.xml | 3 +- .../TestCase/DeleteCustomerAddressTest.php | 3 +- .../TestCase/DeleteCustomerAddressTest.xml | 2 +- .../DeleteCustomerBackendEntityTest.php | 2 +- .../DeleteCustomerBackendEntityTest.xml | 2 +- .../DeleteCustomerGroupEntityTest.php | 2 +- .../DeleteCustomerGroupEntityTest.xml | 2 +- .../DeleteSystemCustomerGroupTest.php | 2 +- .../DeleteSystemCustomerGroupTest.xml | 2 +- .../TestCase/ForgotPasswordOnFrontendTest.php | 2 +- .../TestCase/ForgotPasswordOnFrontendTest.xml | 2 +- .../Test/TestCase/GridFilteringTest.xml | 3 +- .../Test/TestCase/GridFullTextSearchTest.xml | 3 +- .../Test/TestCase/GridSortingTest.xml | 4 +- .../Test/TestCase/LoginOnFrontendFailTest.php | 56 + .../Test/TestCase/LoginOnFrontendFailTest.xml | 15 + .../TestCase/MassAssignCustomerGroupTest.php | 44 +- .../TestCase/MassAssignCustomerGroupTest.xml | 16 +- .../MassDeleteCustomerBackendEntityTest.php | 2 +- .../MassDeleteCustomerBackendEntityTest.xml | 3 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../TestCase/PasswordAutocompleteOffTest.php | 88 + .../TestCase/PasswordAutocompleteOffTest.xml | 17 + .../RegisterCustomerFrontendEntityTest.php | 2 +- .../RegisterCustomerFrontendEntityTest.xml | 5 +- .../UpdateCustomerBackendEntityTest.php | 2 +- .../UpdateCustomerBackendEntityTest.xml | 4 +- .../UpdateCustomerFrontendEntityTest.php | 8 +- .../UpdateCustomerFrontendEntityTest.xml | 10 +- .../UpdateCustomerGroupEntityTest.php | 3 +- .../UpdateCustomerGroupEntityTest.xml | 2 +- .../VerifyDisabledCustomerGroupFieldTest.php | 2 +- .../VerifyDisabledCustomerGroupFieldTest.xml | 2 +- .../Test/TestStep/CreateCustomerStep.php | 6 +- .../CreateOrderFromCustomerAccountStep.php | 2 +- .../TestStep/LoginCustomerOnFrontendStep.php | 2 +- .../TestStep/LogoutCustomerOnFrontendStep.php | 2 +- .../TestStep/OpenCustomerOnBackendStep.php | 2 +- .../app/Magento/Customer/Test/etc/curl/di.xml | 2 +- .../app/Magento/Customer/Test/etc/di.xml | 2 +- .../Magento/Customer/Test/etc/testcase.xml | 8 +- .../Magento/Customer/Test/etc/webapi/di.xml | 2 +- .../Dhl/Test/Repository/ConfigData.xml | 2 +- .../TestCase/CityBasedShippingRateTest.xml | 44 + .../Dhl/Test/TestCase/OnePageCheckoutTest.xml | 4 +- .../Test/Block/Currency/Switcher.php | 2 +- ...AssertCurrencyRateAppliedOnCatalogPage.php | 2 +- ...AssertCurrencyRateAppliedOnProductPage.php | 62 + .../AssertCurrencyRateSuccessSaveMessage.php | 2 +- .../AssertShippingPriceWithCustomCurrency.php | 66 + .../Directory/Test/Fixture/CurrencyRate.xml | 2 +- .../Test/Handler/CurrencyRate/Curl.php | 2 +- .../CurrencyRate/CurrencyRateInterface.php | 2 +- .../Directory/Test/Repository/ConfigData.xml | 33 + .../Test/Repository/CurrencyRate.xml | 2 +- .../Test/TestCase/CreateCurrencyRateTest.php | 32 +- .../Test/TestCase/CreateCurrencyRateTest.xml | 30 +- .../Magento/Directory/Test/etc/curl/di.xml | 2 +- .../app/Magento/Directory/Test/etc/di.xml | 2 +- .../Product/Edit/Section/Downloadable.php | 2 +- .../Edit/Section/Downloadable/LinkRow.php | 2 +- .../Edit/Section/Downloadable/LinkRow.xml | 2 +- .../Edit/Section/Downloadable/Links.php | 2 +- .../Edit/Section/Downloadable/Links.xml | 2 +- .../Edit/Section/Downloadable/SampleRow.php | 2 +- .../Edit/Section/Downloadable/SampleRow.xml | 2 +- .../Edit/Section/Downloadable/Samples.php | 2 +- .../Edit/Section/Downloadable/Samples.xml | 2 +- .../Adminhtml/Product/Composite/Configure.php | 2 +- .../Adminhtml/Product/Composite/Configure.xml | 2 +- .../Block/Adminhtml/Product/ProductForm.xml | 2 +- .../Test/Block/Catalog/Product/View.php | 2 +- .../Test/Block/Catalog/Product/View/Links.php | 2 +- .../Block/Catalog/Product/View/Samples.php | 2 +- .../Block/Customer/Products/ListProducts.php | 2 +- ...axCalculationAfterCheckoutDownloadable.php | 2 +- ...axRuleIsAppliedToAllPricesDownloadable.php | 2 +- .../AssertDownloadableDuplicateForm.php | 2 +- .../AssertDownloadableLinksData.php | 2 +- .../AssertDownloadableProductForm.php | 2 +- ...ProductInCustomerWishlistOnBackendGrid.php | 2 +- .../AssertDownloadableSamplesData.php | 2 +- ...ckoutDownloadableExcludingIncludingTax.php | 2 +- ...nAfterCheckoutDownloadableExcludingTax.php | 2 +- ...nAfterCheckoutDownloadableIncludingTax.php | 2 +- ...ricesDownloadableExcludingIncludingTax.php | 2 +- ...iedToAllPricesDownloadableExcludingTax.php | 2 +- ...iedToAllPricesDownloadableIncludingTax.php | 2 +- .../Downloadable/Test/Fixture/Cart/Item.php | 23 +- .../Test/Fixture/DownloadableProduct.xml | 4 +- .../Test/Handler/DownloadableProduct/Curl.php | 2 +- .../DownloadableProductInterface.php | 2 +- .../Handler/DownloadableProduct/Webapi.php | 2 +- .../Test/Page/Adminhtml/CustomerIndexEdit.xml | 2 +- .../Test/Page/Adminhtml/OrderCreateIndex.xml | 2 +- .../Page/DownloadableCustomerProducts.xml | 2 +- .../Test/Page/Product/CatalogProductView.xml | 2 +- .../Test/Repository/DownloadableProduct.xml | 31 +- .../DownloadableProduct/CheckoutData.xml | 6 +- .../Repository/DownloadableProduct/Links.xml | 2 +- .../DownloadableProduct/Samples.xml | 2 +- .../CreateDownloadableProductEntityTest.php | 2 +- .../CreateDownloadableProductEntityTest.xml | 12 +- .../Test/TestCase/DeleteProductEntityTest.xml | 2 +- .../DeleteProductFromMiniShoppingCartTest.xml | 2 +- .../TestCase/DuplicateProductEntityTest.xml | 3 +- .../Test/TestCase/TaxCalculationTest.xml | 6 +- .../UpdateDownloadableProductEntityTest.php | 66 +- .../UpdateDownloadableProductEntityTest.xml | 13 +- .../ValidateOrderOfProductTypeTest.xml | 2 +- .../Magento/Downloadable/Test/etc/curl/di.xml | 2 +- .../app/Magento/Downloadable/Test/etc/di.xml | 2 +- .../Downloadable/Test/etc/webapi/di.xml | 2 +- .../Adminhtml/Template/Edit/TemplateForm.php | 2 +- .../Adminhtml/Template/Edit/TemplateForm.xml | 4 +- .../AssertEmailTemplateSuccessSaveMessage.php | 2 +- .../Email/Test/Fixture/EmailTemplate.xml | 2 +- .../Page/Adminhtml/EmailTemplateIndex.xml | 2 +- .../Test/Page/Adminhtml/EmailTemplateNew.xml | 4 +- .../CreateEmailTemplateEntityTest.php | 3 +- .../CreateEmailTemplateEntityTest.xml | 4 +- .../Email/Test/TestCase/NavigateMenuTest.xml | 2 +- .../Fedex/Test/Repository/ConfigData.xml | 2 +- .../TestCase/CityBasedShippingRateTest.xml | 44 + .../Test/TestCase/OnePageCheckoutTest.xml | 6 +- .../Test/Block/Adminhtml/Order/Create.php | 2 +- .../Block/Adminhtml/Order/Create/Form.php | 2 +- .../Block/Adminhtml/Order/Create/Form.xml | 2 +- .../Adminhtml/Order/Create/GiftOptions.php | 2 +- .../Adminhtml/Order/Create/GiftOptions.xml | 2 +- .../Block/Adminhtml/Order/Create/Items.php | 2 +- .../Order/Create/Items/ItemProduct.php | 2 +- .../Test/Block/Adminhtml/Order/View/Form.php | 2 +- .../Test/Block/Adminhtml/Order/View/Form.xml | 2 +- .../Adminhtml/Order/View/GiftOptions.php | 2 +- .../Adminhtml/Order/View/GiftOptions.xml | 2 +- .../Test/Block/Adminhtml/Order/View/Items.php | 2 +- .../Order/View/Items/ItemProduct.php | 2 +- .../Test/Block/Cart/GiftOptions.php | 2 +- .../Cart/GiftOptions/GiftMessageForm.php | 2 +- .../Cart/GiftOptions/GiftMessageForm.xml | 2 +- .../Test/Block/Cart/Item/GiftOptions.php | 2 +- .../Test/Block/Cart/Item/GiftOptions.xml | 2 +- .../Test/Block/Message/Order/Items/View.php | 2 +- .../Test/Block/Message/Order/View.php | 2 +- .../AssertGiftMessageInBackendOrder.php | 2 +- .../AssertGiftMessageInFrontendOrder.php | 2 +- .../AssertGiftMessageInFrontendOrderItems.php | 2 +- .../GiftMessage/Test/Fixture/GiftMessage.xml | 2 +- .../Test/Fixture/GiftMessage/Items.php | 2 +- .../Test/Page/Adminhtml/OrderCreateIndex.xml | 2 +- .../Test/Page/Adminhtml/OrderView.xml | 2 +- .../GiftMessage/Test/Page/CheckoutCart.xml | 2 +- .../Test/Page/CustomerOrderView.xml | 2 +- .../Test/Repository/ConfigData.xml | 2 +- .../Test/Repository/GiftMessage.xml | 2 +- .../TestCase/CheckoutWithGiftMessagesTest.php | 3 +- .../TestCase/CheckoutWithGiftMessagesTest.xml | 4 +- .../CreateGiftMessageOnBackendTest.php | 43 + .../CreateGiftMessageOnBackendTest.xml | 73 + .../TestStep/AddGiftMessageBackendStep.php | 2 +- .../Test/TestStep/AddGiftMessageStep.php | 2 +- .../app/Magento/GiftMessage/Test/etc/di.xml | 8 +- .../Magento/GiftMessage/Test/etc/testcase.xml | 2 +- .../Adminhtml/Product/Composite/Configure.php | 2 +- .../Adminhtml/Product/Composite/Configure.xml | 4 +- .../Product/Grouped/AssociatedProducts.php | 2 +- .../ListAssociatedProducts.php | 2 +- .../ListAssociatedProducts/Product.php | 2 +- .../ListAssociatedProducts/Product.xml | 2 +- .../AssociatedProducts/Search/Grid.php | 2 +- .../Block/Adminhtml/Product/ProductForm.xml | 2 +- .../Test/Block/Cart/Sidebar.php | 2 +- .../Test/Block/Cart/Sidebar/Item.php | 2 +- .../Test/Block/Catalog/Product/View.php | 17 +- .../Catalog/Product/View/Type/Grouped.php | 14 +- .../Test/Block/Checkout/Cart.php | 2 +- .../Test/Block/Checkout/Cart/CartItem.php | 56 +- ...bstractAssertPriceOnGroupedProductPage.php | 2 +- ...AppliedToAllPricesOnGroupedProductPage.php | 82 + .../Constraint/AssertGroupedProductForm.php | 2 +- ...ProductInCustomerWishlistOnBackendGrid.php | 2 +- ...AssertGroupedProductInItemsOrderedGrid.php | 2 +- .../AssertGroupedProductsDefaultQty.php | 2 +- .../AssertProductInItemsOrderedGrid.php | 57 + ...AssertSpecialPriceOnGroupedProductPage.php | 2 +- ...oAllPricesGroupedExcludingIncludingTax.php | 74 + .../AssertTierPriceOnGroupedProductPage.php | 2 +- .../GroupedProduct/Test/Fixture/Cart/Item.php | 20 +- .../Test/Fixture/GroupedProduct.xml | 2 +- .../Fixture/GroupedProduct/Associated.php | 2 +- .../Test/Handler/GroupedProduct/Curl.php | 2 +- .../GroupedProductInterface.php | 2 +- .../Test/Handler/GroupedProduct/Webapi.php | 2 +- .../Test/Page/Adminhtml/CustomerIndexEdit.xml | 2 +- .../Test/Page/Adminhtml/OrderCreateIndex.xml | 2 +- .../GroupedProduct/Test/Page/CheckoutCart.xml | 2 +- .../GroupedProduct/Test/Page/CmsIndex.xml | 2 +- .../Test/Page/Product/CatalogProductView.xml | 2 +- .../Test/Repository/GroupedProduct.xml | 148 +- .../Repository/GroupedProduct/Associated.xml | 44 +- .../GroupedProduct/CheckoutData.xml | 29 +- .../Test/Repository/GroupedProduct/Price.xml | 2 +- .../CreateGroupedProductEntityTest.php | 2 +- .../CreateGroupedProductEntityTest.xml | 4 +- .../Test/TestCase/DeleteProductEntityTest.xml | 2 +- .../DeleteProductFromMiniShoppingCartTest.xml | 3 +- ...ecentlyComparedProductsOnOrderPageTest.xml | 17 + .../Test/TestCase/TaxCalculationTest.xml | 41 + .../UpdateGroupedProductEntityTest.php | 2 +- .../UpdateGroupedProductEntityTest.xml | 7 +- .../ValidateOrderOfProductTypeTest.xml | 2 +- .../GroupedProduct/Test/etc/curl/di.xml | 2 +- .../Magento/GroupedProduct/Test/etc/di.xml | 22 + .../GroupedProduct/Test/etc/webapi/di.xml | 2 +- .../Mtf/Util/Import/File/CsvTemplate.php | 90 + .../Test/Block/Adminhtml/Export/Edit/Form.php | 2 +- .../Test/Block/Adminhtml/Export/Edit/Form.xml | 2 +- .../Test/Block/Adminhtml/Export/Filter.php | 2 +- .../Test/Block/Adminhtml/Import/Edit/Form.php | 14 + .../Test/Block/Adminhtml/Import/Edit/Form.xml | 34 + .../Adminhtml/Import/FormPageActions.php | 40 + .../Block/Adminhtml/Import/Frame/Result.php | 62 + .../AssertImportCheckDataErrorMessage.php | 51 + ...AssertProductAttributeAbsenceForExport.php | 10 +- .../{ImportExport.xml => ExportData.xml} | 8 +- .../ImportExport/Test/Fixture/Import/File.php | 155 + .../ImportExport/Test/Fixture/ImportData.xml | 26 + .../Test/Page/Adminhtml/AdminExportIndex.xml | 2 +- .../Test/Page/Adminhtml/AdminImportIndex.xml | 14 + .../{ImportExport.xml => ExportData.xml} | 4 +- .../Test/TestCase/ImportDataNegativeTest.php | 38 + .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../Test/TestStep/ClickCheckDataStep.php | 40 + .../Test/TestStep/FillImportFormStep.php | 58 + .../Test/TestStep/OpenImportIndexStep.php | 40 + .../app/Magento/ImportExport/Test/etc/di.xml | 30 +- .../ImportExport/Test/etc/testcase.xml | 15 + .../Block/Adminhtml/IndexManagement/Grid.php | 83 + .../Test/Constraint/AssertIndexerStatus.php | 60 + ...sertUpdateByScheduleSuccessSaveMessage.php | 50 + .../Test/Page/Adminhtml/IndexManagement.xml | 13 + .../CreateCatalogRulesIndexerTest.php | 284 + .../CreateCatalogRulesIndexerTest.xml | 27 + .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../Install/Test/Block/CreateAdmin.php | 2 +- .../Install/Test/Block/CreateAdmin.xml | 2 +- .../Install/Test/Block/CustomizeStore.php | 2 +- .../Install/Test/Block/CustomizeStore.xml | 2 +- .../Magento/Install/Test/Block/Database.php | 2 +- .../Magento/Install/Test/Block/Database.xml | 2 +- .../Magento/Install/Test/Block/Devdocs.php | 32 + .../Magento/Install/Test/Block/Install.php | 2 +- .../Magento/Install/Test/Block/Landing.php | 20 +- .../Magento/Install/Test/Block/License.php | 2 +- .../Magento/Install/Test/Block/Readiness.php | 2 +- .../Install/Test/Block/WebConfiguration.php | 4 +- .../Install/Test/Block/WebConfiguration.xml | 2 +- .../AssertAdminUriAutogenerated.php | 2 +- .../Constraint/AssertAgreementTextPresent.php | 2 +- .../Constraint/AssertCurrencySelected.php | 2 +- .../Test/Constraint/AssertDevdocsLink.php | 46 + .../AssertGenerationFilePathCheck.php | 64 + .../Test/Constraint/AssertKeyCreated.php | 2 +- .../Constraint/AssertLanguageSelected.php | 2 +- .../Test/Constraint/AssertRewritesEnabled.php | 2 +- .../Constraint/AssertSecureUrlEnabled.php | 2 +- .../Test/Constraint/AssertSuccessInstall.php | 2 +- .../AssertSuccessfulReadinessCheck.php | 2 +- .../Magento/Install/Test/Fixture/Install.xml | 2 +- .../Install/Test/Page/DevdocsInstall.xml | 12 + .../app/Magento/Install/Test/Page/Install.xml | 2 +- .../Install/Test/TestCase/InstallTest.php | 114 +- .../Install/Test/TestCase/InstallTest.xml | 29 +- .../Integration/Edit/IntegrationForm.php | 2 +- .../Integration/Edit/IntegrationForm.xml | 2 +- .../Edit/IntegrationFormPageActions.php | 2 +- .../Adminhtml/Integration/Edit/Tab/Api.php | 2 +- .../Adminhtml/Integration/IntegrationGrid.php | 2 +- .../IntegrationGrid/DeleteDialog.php | 2 +- .../IntegrationGrid/ResourcesPopup.php | 2 +- .../IntegrationGrid/ResourcesPopup.xml | 2 +- .../IntegrationGrid/TokensPopup.php | 2 +- .../IntegrationGrid/TokensPopup.xml | 2 +- .../AssertEmailValidationErrorGenerated.php | 2 +- .../AssertIncorrectUserPassword.php | 2 +- .../Test/Constraint/AssertIntegrationForm.php | 2 +- .../Constraint/AssertIntegrationInGrid.php | 2 +- ...IntegrationNameDuplicationErrorMessage.php | 2 +- .../Constraint/AssertIntegrationNotInGrid.php | 2 +- .../AssertIntegrationResourcesPopup.php | 2 +- ...ertIntegrationSuccessActivationMessage.php | 2 +- .../AssertIntegrationSuccessDeleteMessage.php | 2 +- ...rtIntegrationSuccessReauthorizeMessage.php | 2 +- .../AssertIntegrationSuccessSaveMessage.php | 2 +- ...ntegrationSuccessSaveMessageNotPresent.php | 2 +- ...ssertIntegrationTokensAfterReauthorize.php | 2 +- .../AssertIntegrationTokensPopup.php | 2 +- .../Test/Constraint/AssertNoAlertPopup.php | 2 +- .../Integration/Test/Fixture/Integration.xml | 2 +- .../Test/Handler/Integration/Curl.php | 2 +- .../Integration/IntegrationInterface.php | 2 +- .../Test/Page/Adminhtml/IntegrationIndex.xml | 2 +- .../Test/Page/Adminhtml/IntegrationNew.xml | 2 +- .../Test/Repository/Integration.xml | 2 +- .../ActivateIntegrationEntityTest.php | 2 +- .../ActivateIntegrationEntityTest.xml | 2 +- .../TestCase/CreateIntegrationEntityTest.php | 2 +- .../TestCase/CreateIntegrationEntityTest.xml | 2 +- ...reateIntegrationWithDuplicatedNameTest.php | 2 +- ...reateIntegrationWithDuplicatedNameTest.xml | 2 +- .../TestCase/DeleteIntegrationEntityTest.php | 2 +- .../TestCase/DeleteIntegrationEntityTest.xml | 3 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- ...ReAuthorizeTokensIntegrationEntityTest.php | 2 +- ...ReAuthorizeTokensIntegrationEntityTest.xml | 2 +- .../TestCase/UpdateIntegrationEntityTest.php | 2 +- .../TestCase/UpdateIntegrationEntityTest.xml | 3 +- .../Magento/Integration/Test/etc/curl/di.xml | 2 +- .../app/Magento/Integration/Test/etc/di.xml | 2 +- .../Test/Block/Navigation.php | 2 +- ...rtCategorySortingOnFilteredProductList.php | 87 + .../Constraint/AssertFilterProductList.php | 2 +- .../Page/Category/CatalogCategoryView.xml | 2 +- .../Test/Repository/ConfigData.xml | 60 +- .../Test/TestCase/FilterProductListTest.php | 37 +- .../Test/TestCase/FilterProductListTest.xml | 117 +- .../Msrp/Test/Block/Product/ListProduct.php | 2 +- .../Magento/Msrp/Test/Block/Product/Map.php | 2 +- .../Block/Product/ProductList/ProductItem.php | 2 +- .../Magento/Msrp/Test/Block/Product/View.php | 2 +- .../Constraint/AssertMsrpInShoppingCart.php | 2 +- .../Constraint/AssertMsrpOnCategoryPage.php | 2 +- .../Constraint/AssertMsrpOnProductView.php | 2 +- .../Page/Category/CatalogCategoryView.xml | 2 +- .../Test/Page/Product/CatalogProductView.xml | 2 +- .../Test/Repository/CatalogProductSimple.xml | 2 +- .../Msrp/Test/Repository/ConfigData.xml | 2 +- .../Test/Repository/ConfigurableProduct.xml | 2 +- .../Msrp/Test/TestCase/ApplyMapTest.php | 2 +- .../Msrp/Test/TestCase/ApplyMapTest.xml | 2 +- .../Test/Block/Checkout/Addresses.php | 2 +- .../Test/Block/Checkout/Billing.php | 2 +- .../Test/Block/Checkout/Link.php | 2 +- .../Test/Block/Checkout/Overview.php | 2 +- .../Test/Block/Checkout/Shipping.php | 2 +- .../Test/Block/Checkout/Success.php | 2 +- ...MultishippingOrderSuccessPlacedMessage.php | 2 +- .../Multishipping/Test/Page/CheckoutCart.xml | 4 +- ...ultishippingCheckoutAddressNewShipping.php | 2 +- .../Page/MultishippingCheckoutAddresses.xml | 4 +- .../Page/MultishippingCheckoutBilling.xml | 4 +- .../Test/Page/MultishippingCheckoutCart.php | 2 +- .../Test/Page/MultishippingCheckoutLogin.php | 2 +- .../Page/MultishippingCheckoutOverview.xml | 4 +- .../Page/MultishippingCheckoutRegister.php | 2 +- .../Page/MultishippingCheckoutShipping.xml | 4 +- .../Page/MultishippingCheckoutSuccess.xml | 4 +- .../TestStep/FillCustomerAddressesStep.php | 2 +- .../TestStep/FillShippingInformationStep.php | 2 +- .../Test/TestStep/PlaceOrderStep.php | 2 +- .../ProceedToMultipleAddressCheckoutStep.php | 2 +- .../Test/TestStep/SelectPaymentMethodStep.php | 2 +- .../app/Magento/Multishipping/Test/etc/di.xml | 4 +- .../Block/Adminhtml/Queue/Edit/QueueForm.php | 2 +- .../Test/Block/Adminhtml/Subscriber/Grid.php | 2 +- .../Adminhtml/Template/FormPageActions.php | 2 +- .../Test/Block/Adminhtml/Template/Grid.php | 2 +- .../Adminhtml/Template/GridPageActions.php | 2 +- .../Test/Block/Adminhtml/Template/Preview.php | 2 +- ...AssertCustomerIsSubscribedToNewsletter.php | 2 +- .../Test/Constraint/AssertNewsletterForm.php | 2 +- .../Constraint/AssertNewsletterInGrid.php | 2 +- .../Constraint/AssertNewsletterPreview.php | 2 +- .../Test/Constraint/AssertNewsletterQueue.php | 2 +- .../AssertNewsletterSuccessCreateMessage.php | 2 +- .../Newsletter/Test/Fixture/Template.xml | 2 +- .../Newsletter/Test/Handler/Template/Curl.php | 2 +- .../Handler/Template/TemplateInterface.php | 2 +- .../Test/Page/Adminhtml/SubscriberIndex.xml | 2 +- .../Test/Page/Adminhtml/TemplateEdit.xml | 2 +- .../Test/Page/Adminhtml/TemplateIndex.xml | 2 +- .../Test/Page/Adminhtml/TemplateNewIndex.xml | 2 +- .../Test/Page/Adminhtml/TemplatePreview.xml | 2 +- .../Test/Page/Adminhtml/TemplateQueue.xml | 2 +- .../Newsletter/Test/Repository/Customer.xml | 24 + .../Newsletter/Test/Repository/Template.xml | 2 +- .../ActionNewsletterTemplateEntityTest.php | 3 +- .../ActionNewsletterTemplateEntityTest.xml | 2 +- .../CreateNewsletterTemplateEntityTest.php | 2 +- .../CreateNewsletterTemplateEntityTest.xml | 2 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../PreviewNewsletterTemplateEntityTest.php | 2 +- .../PreviewNewsletterTemplateEntityTest.xml | 3 +- .../TestCase/UpdateNewsletterTemplateTest.php | 2 +- .../TestCase/UpdateNewsletterTemplateTest.xml | 2 +- .../Magento/Newsletter/Test/etc/curl/di.xml | 2 +- .../Test/Repository/ConfigData.xml | 17 +- .../Test/Repository/ConfigData.xml | 2 +- .../Magento/PageCache/Test/Block/Cache.php | 2 +- .../PageCache/Test/Block/Cache/Additional.php | 2 +- .../PageCache/Test/Block/Cache/Grid.php | 2 +- .../AssertCacheFlushSuccessMessage.php | 2 +- .../AssertCacheInvalidateNotice.php | 2 +- .../Constraint/AssertCacheInvalidatePopUp.php | 2 +- ...AssertCacheIsRefreshableAndInvalidated.php | 2 +- .../Test/Constraint/AssertCacheStatus.php | 3 +- .../Test/Constraint/AssertCategoryCaching.php | 63 + ...tFlushStaticFilesCacheButtonVisibility.php | 2 +- .../Test/Page/Adminhtml/AdminCache.xml | 2 +- .../Test/TestCase/CacheInvalidationTest.php | 109 + .../Test/TestCase/CacheInvalidationTest.xml | 17 + .../CacheStatusOnScheduledIndexingTest.php | 136 + .../CacheStatusOnScheduledIndexingTest.xml | 18 + .../TestCase/FlushAdditionalCachesTest.php | 2 +- .../TestCase/FlushAdditionalCachesTest.xml | 2 +- ...shStaticFilesCacheButtonVisibilityTest.php | 2 +- ...shStaticFilesCacheButtonVisibilityTest.xml | 2 +- .../app/Magento/PageCache/Test/etc/di.xml | 2 +- .../Magento/Payment/Test/Block/Form/Cc.php | 17 - .../Test/Block/Form/PaymentCc.php} | 20 +- .../Test/Block/Form/{Cc.xml => PaymentCc.xml} | 2 +- .../Constraint/AssertCardRequiredFields.php | 67 + .../Test/Constraint/AssertFieldsAreActive.php | 2 +- .../Constraint/AssertFieldsAreDisabled.php | 2 +- .../Constraint/AssertFieldsAreEnabled.php | 2 +- .../Constraint/AssertFieldsArePresent.php | 2 +- .../Payment/Test/Fixture/CreditCard.xml | 4 +- .../Payment/Test/Fixture/CreditCardAdmin.xml | 21 - .../Payment/Test/Repository/CreditCard.xml | 39 +- .../Test/Repository/CreditCardAdmin.xml | 26 - .../Test/TestCase/ConflictResolutionTest.php | 3 +- .../Test/TestCase/ConflictResolutionTest.xml | 4 +- .../tests/app/Magento/Payment/Test/etc/di.xml | 34 + .../app/Magento/Payment/Test/etc/fixture.xml | 2 +- .../Paypal/Test/Block/Express/Review.php | 2 +- .../Review/ShippingoptgroupElement.php | 2 +- .../Paypal/Test/Block/Form/HostedPro/Cc.php | 17 + .../Paypal/Test/Block/Form/HostedPro/Cc.xml | 27 + .../Paypal/Test/Block/Form/PayflowLink/Cc.php | 17 + .../Paypal/Test/Block/Form/PayflowLink/Cc.xml | 23 + .../Test/Block/Form/PaymentsAdvanced/Cc.php | 17 + .../Test/Block/Form/PaymentsAdvanced/Cc.xml | 23 + .../Test/Block/Onepage/Payment/HostedPro.php | 20 + .../Block/Onepage/Payment/PayflowLink.php | 20 + .../Onepage/Payment/PaymentsAdvanced.php | 20 + .../Block/Onepage/Payment/PaypalIframe.php | 98 + .../Test/Block/Sandbox/ExpressLogin.php | 2 +- .../Test/Block/Sandbox/ExpressLogin.xml | 2 +- .../Test/Block/Sandbox/ExpressMainLogin.php | 2 +- .../Test/Block/Sandbox/ExpressMainReview.php | 2 +- .../Test/Block/Sandbox/ExpressOldLogin.php | 2 +- .../Test/Block/Sandbox/ExpressOldLogin.xml | 2 +- .../Test/Block/Sandbox/ExpressOldReview.php | 2 +- .../Test/Block/Sandbox/ExpressReview.php | 2 +- .../Test/Block/Sandbox/SignupAddCard.php | 2 +- .../Test/Block/Sandbox/SignupAddCard.xml | 2 +- .../Test/Block/Sandbox/SignupCreate.php | 2 +- .../Test/Block/Sandbox/SignupCreate.xml | 2 +- .../Block/Sandbox/SignupPersonalAccount.php | 2 +- .../Block/Sandbox/SignupPersonalAccount.xml | 2 +- .../Block/System/Config/ExpressCheckout.php | 7 +- .../Test/Block/System/Config/PayflowLink.php | 7 +- .../Test/Block/System/Config/PayflowPro.php | 7 +- .../Block/System/Config/PaymentsAdvanced.php | 7 +- .../Test/Block/System/Config/PaymentsPro.php | 7 +- .../AssertExpressCancelledMessage.php | 2 +- .../Sandbox/AssertTotalPaypalReview.php | 2 +- .../Paypal/Test/Fixture/SandboxCustomer.xml | 2 +- .../SystemConfigEditSectionPayment.xml | 2 +- .../Paypal/Test/Page/CheckoutOnepage.xml | 14 + .../Paypal/Test/Page/OrderReviewExpress.xml | 2 +- .../Test/Page/Sandbox/AccountSignup.xml | 2 +- .../Test/Page/Sandbox/ExpressReview.xml | 2 +- .../Test/Page/Sandbox/SignupAddCard.xml | 2 +- .../Paypal/Test/Page/Sandbox/SignupCreate.xml | 2 +- .../Paypal/Test/Repository/ConfigData.xml | 340 +- .../Test/Repository/CreditCard.xml | 11 +- .../Test/Repository/SandboxCustomer.xml | 2 +- .../Paypal/Test/TestCase/CloseOrderTest.xml | 57 + .../TestCase/CloseSalesWithHostedProTest.php | 44 + .../TestCase/CloseSalesWithHostedProTest.xml | 38 + .../TestCase/CreateOnlineCreditMemoTest.xml | 89 + .../CreatePayFlowOrderBackendNegativeTest.php | 50 + .../CreatePayFlowOrderBackendNegativeTest.xml | 28 + .../TestCase/CreateVaultOrderBackendTest.xml | 8 +- .../ExpressCheckoutFromProductPageTest.php | 3 +- .../ExpressCheckoutFromProductPageTest.xml | 4 +- .../ExpressCheckoutFromShoppingCartTest.php | 3 +- .../ExpressCheckoutFromShoppingCartTest.xml | 4 +- .../TestCase/ExpressCheckoutOnePageTest.php | 3 +- .../TestCase/ExpressCheckoutOnePageTest.xml | 8 +- ...extExpressCheckoutFromShoppingCartTest.php | 3 +- ...extExpressCheckoutFromShoppingCartTest.xml | 4 +- .../InContextExpressOnePageCheckoutTest.php | 3 +- .../InContextExpressOnePageCheckoutTest.xml | 4 +- .../Paypal/Test/TestCase/NavigateMenuTest.xml | 23 + .../TestCase/OnePageCheckoutDeclinedTest.xml | 53 + .../TestCase/OnePageCheckoutHostedProTest.php | 51 + .../TestCase/OnePageCheckoutHostedProTest.xml | 35 + .../OnePageCheckoutPayflowLinkTest.php | 51 + .../OnePageCheckoutPayflowLinkTest.xml | 34 + .../OnePageCheckoutPaymentsAdvancedTest.php | 51 + .../OnePageCheckoutPaymentsAdvancedTest.xml | 34 + .../Test/TestCase/OnePageCheckoutTest.xml | 144 + .../Test/TestCase/ReorderUsingVaultTest.xml | 8 +- .../Test/TestCase/UseVaultOnCheckoutTest.xml | 6 +- .../Test/TestStep/CheckExpressConfigStep.php | 2 +- .../TestStep/CheckPayflowLinkConfigStep.php | 2 +- .../TestStep/CheckPayflowProConfigStep.php | 2 +- .../CheckPaymentsAdvancedConfigStep.php | 2 +- .../TestStep/CheckPaymentsProConfigStep.php | 2 +- .../CheckoutWithPaypalFromProductPageStep.php | 2 +- ...CheckoutWithPaypalFromShoppingCartStep.php | 2 +- .../TestStep/ContinuePaypalCheckoutStep.php | 2 +- .../ContinueToPaypalInContextStep.php | 2 +- .../Test/TestStep/ContinueToPaypalStep.php | 2 +- .../TestStep/CreateSandboxCustomerStep.php | 2 +- .../ExpressCheckoutOrderPlaceStep.php | 2 +- .../Test/TestStep/GetPlacedOrderIdStep.php | 44 - ...CheckoutWithPaypalFromShoppingCartStep.php | 2 +- .../TestStep/PlaceOrderWithHostedProStep.php | 104 + .../PlaceOrderWithPayflowLinkStep.php | 98 + .../PlaceOrderWithPaymentsAdvancedStep.php | 98 + .../tests/app/Magento/Paypal/Test/etc/di.xml | 19 + .../app/Magento/Paypal/Test/etc/testcase.xml | 76 +- .../AssertCustomerIsRedirectedToCheckout.php | 42 + .../Persistent/Test/Repository/ConfigData.xml | 40 + ...CheckoutWithPersistentShoppingCartTest.php | 181 + ...CheckoutWithPersistentShoppingCartTest.xml | 17 + .../Product/Edit/Tab/Images/VideoDialog.php | 2 +- .../Product/Edit/Tab/Images/VideoDialog.xml | 2 +- .../Product/Edit/Tab/ImagesAndVideos.php | 2 +- .../Block/Adminhtml/Product/ProductForm.xml | 2 +- .../AssertGetVideoInfoDataIsCorrect.php | 2 +- .../Constraint/AssertNoVideoCategoryView.php | 2 +- .../Constraint/AssertNoVideoProductView.php | 2 +- .../Constraint/AssertVideoCategoryView.php | 2 +- .../Constraint/AssertVideoProductView.php | 2 +- .../Test/Fixture/CatalogProductSimple.xml | 2 +- .../Test/Fixture/Product/MediaGallery.php | 2 +- .../Test/Repository/CatalogProductSimple.xml | 2 +- .../Test/Repository/ConfigData.xml | 2 +- .../Test/TestCase/AddProductVideoTest.php | 2 +- .../Test/TestCase/AddProductVideoTest.xml | 4 +- .../Test/TestCase/DeleteProductVideoTest.php | 2 +- .../Test/TestCase/DeleteProductVideoTest.xml | 4 +- .../Test/TestCase/UpdateProductVideoTest.php | 5 +- .../Test/TestCase/UpdateProductVideoTest.xml | 2 +- .../Test/Block/Adminhtml/AbstractFilter.php | 2 +- .../Block/Adminhtml/Customer/AccountsGrid.php | 2 +- .../Adminhtml/Customer/Counts/Filter.php | 2 +- .../Adminhtml/Customer/Counts/Filter.xml | 2 +- .../Block/Adminhtml/Customer/Counts/Grid.php | 2 +- .../Adminhtml/Customer/Totals/Filter.php | 2 +- .../Adminhtml/Customer/Totals/Filter.xml | 2 +- .../Block/Adminhtml/Customer/Totals/Grid.php | 2 +- .../Adminhtml/Product/Downloads/Grid.php | 2 +- .../Block/Adminhtml/Product/Lowstock/Grid.php | 2 +- .../Block/Adminhtml/Product/Sold/Grid.php | 2 +- .../Block/Adminhtml/Product/Viewed/Filter.php | 2 +- .../Block/Adminhtml/Product/Viewed/Filter.xml | 2 +- .../Adminhtml/Product/Viewed/ProductGrid.php | 2 +- .../Adminhtml/Refresh/Statistics/Grid.php | 31 +- .../Block/Adminhtml/Review/Customer/Grid.php | 2 +- .../Block/Adminhtml/Review/Products/Grid.php | 2 +- .../Review/Products/Viewed/Filter.php | 2 +- .../Review/Products/Viewed/Filter.xml | 2 +- .../Review/Products/Viewed/ProductGrid.php | 2 +- .../Block/Adminhtml/Sales/Coupons/Action.php | 2 +- .../Block/Adminhtml/Sales/Coupons/Filter.php | 2 +- .../Block/Adminhtml/Sales/Coupons/Filter.xml | 2 +- .../Block/Adminhtml/Sales/Coupons/Grid.php | 2 +- .../Block/Adminhtml/Sales/Invoiced/Grid.php | 2 +- .../Sales/Orders/Viewed/FilterGrid.php | 2 +- .../Adminhtml/Sales/Refunded/FilterGrid.php | 2 +- .../Block/Adminhtml/Sales/Shipping/Grid.php | 33 + .../Block/Adminhtml/Sales/TaxRule/Action.php | 2 +- .../Block/Adminhtml/Sales/TaxRule/Filter.php | 2 +- .../Block/Adminhtml/Sales/TaxRule/Filter.xml | 2 +- .../Block/Adminhtml/Sales/TaxRule/Grid.php | 2 +- .../Test/Block/Adminhtml/SearchTermsGrid.php | 2 +- .../Adminhtml/Shopcart/Abandoned/Grid.php | 2 +- .../Block/Adminhtml/Shopcart/Product/Grid.php | 2 +- .../Test/Block/Adminhtml/Viewed/Action.php | 2 +- ...bstractAssertCustomerOrderReportResult.php | 2 +- .../AbstractAssertInvoiceReportResult.php | 2 +- .../AbstractAssertSalesReportResult.php | 2 +- .../AbstractAssertShippingReportResult.php | 84 + .../AssertAbandonedCartCustomerInfoResult.php | 2 +- .../AssertBestsellerReportResult.php | 2 +- .../Constraint/AssertCouponReportResult.php | 2 +- .../AssertCustomerOrderCountReportResult.php | 2 +- .../AssertCustomerOrderTotalReportResult.php | 2 +- .../AssertDownloadsReportResult.php | 2 +- .../AssertInvoiceReportIntervalResult.php | 2 +- .../AssertInvoiceReportTotalResult.php | 2 +- ...AssertLifetimeStatisticsUpdatedMessage.php | 49 + .../AssertLowStockProductInGrid.php | 2 +- .../AssertNewAccountsReportTotalResult.php | 2 +- .../Constraint/AssertOrderedProductResult.php | 2 +- .../Constraint/AssertProductInCartResult.php | 2 +- .../AssertProductReportByCustomerInGrid.php | 2 +- ...AssertProductReportByCustomerNotInGrid.php | 2 +- ...sertProductReviewIsAvailableForProduct.php | 2 +- ...sertProductReviewReportIsVisibleInGrid.php | 2 +- .../AssertProductReviewsQtyByCustomer.php | 2 +- .../AssertProductViewsReportTotalResult.php | 2 +- .../AssertRecentStatisticsUpdatedMessage.php | 49 + .../AssertRefundReportIntervalResult.php | 2 +- .../AssertReportsUpdatedTimezone.php | 47 + .../AssertSalesReportIntervalResult.php | 2 +- .../AssertSalesReportTotalResult.php | 2 +- .../Constraint/AssertSearchTermReportForm.php | 2 +- .../Constraint/AssertSearchTermsInGrid.php | 2 +- .../AssertShippingReportIntervalResult.php | 47 + .../AssertShippingReportTotalResult.php | 47 + .../Test/Constraint/AssertTaxReportInGrid.php | 2 +- .../Constraint/AssertTaxReportNotInGrid.php | 2 +- .../Test/Page/Adminhtml/AbandonedCarts.xml | 2 +- .../Test/Page/Adminhtml/Bestsellers.xml | 2 +- .../Test/Page/Adminhtml/CustomerAccounts.xml | 2 +- .../Page/Adminhtml/CustomerOrdersReport.xml | 2 +- .../Page/Adminhtml/CustomerReportReview.xml | 2 +- .../Page/Adminhtml/CustomerTotalsReport.xml | 2 +- .../Test/Page/Adminhtml/DownloadsReport.xml | 2 +- .../Page/Adminhtml/OrderedProductsReport.xml | 2 +- .../Test/Page/Adminhtml/ProductLowStock.xml | 2 +- .../Page/Adminhtml/ProductReportReview.xml | 2 +- .../Test/Page/Adminhtml/ProductReportView.xml | 2 +- .../Test/Page/Adminhtml/RefundsReport.xml | 2 +- .../Page/Adminhtml/SalesCouponReportView.xml | 2 +- .../Page/Adminhtml/SalesInvoiceReport.xml | 2 +- .../Test/Page/Adminhtml/SalesReport.xml | 2 +- .../Page/Adminhtml/SalesShippingReport.xml | 15 + .../Test/Page/Adminhtml/SalesTaxReport.xml | 2 +- .../Test/Page/Adminhtml/SearchIndex.xml | 2 +- .../Page/Adminhtml/ShopCartProductReport.xml | 2 +- .../Test/Page/Adminhtml/Statistics.xml | 3 +- .../Reports/Test/Repository/ConfigData.xml | 14 +- .../AbandonedCartsReportEntityTest.php | 3 +- .../AbandonedCartsReportEntityTest.xml | 2 +- .../BestsellerProductsReportEntityTest.php | 2 +- .../BestsellerProductsReportEntityTest.xml | 2 +- .../CustomersOrderCountReportEntityTest.php | 3 +- .../CustomersOrderCountReportEntityTest.xml | 2 +- .../CustomersOrderTotalReportEntityTest.php | 2 +- .../CustomersOrderTotalReportEntityTest.xml | 2 +- .../DownloadProductsReportEntityTest.php | 2 +- .../DownloadProductsReportEntityTest.xml | 5 +- .../LowStockProductsReportEntityTest.php | 2 +- .../LowStockProductsReportEntityTest.xml | 2 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../TestCase/NewAccountsReportEntityTest.php | 2 +- .../TestCase/NewAccountsReportEntityTest.xml | 5 +- .../OrderedProductsReportEntityTest.php | 3 +- .../OrderedProductsReportEntityTest.xml | 2 +- .../ProductsInCartReportEntityTest.php | 2 +- .../ProductsInCartReportEntityTest.xml | 2 +- .../TestCase/RefreshReportsStatisticsTest.php | 82 + .../TestCase/RefreshReportsStatisticsTest.xml | 25 + .../Test/TestCase/ReviewReportEntityTest.php | 2 +- .../Test/TestCase/ReviewReportEntityTest.xml | 2 +- .../TestCase/SalesCouponReportEntityTest.php | 3 +- .../TestCase/SalesCouponReportEntityTest.xml | 2 +- .../TestCase/SalesInvoiceReportEntityTest.php | 3 +- .../TestCase/SalesInvoiceReportEntityTest.xml | 2 +- .../TestCase/SalesOrderReportEntityTest.php | 2 +- .../TestCase/SalesOrderReportEntityTest.xml | 4 +- .../TestCase/SalesRefundsReportEntityTest.php | 2 +- .../TestCase/SalesRefundsReportEntityTest.xml | 4 +- .../TestCase/SalesTaxReportEntityTest.php | 2 +- .../TestCase/SalesTaxReportEntityTest.xml | 4 +- .../TestCase/SearchTermsReportEntityTest.php | 2 +- .../TestCase/SearchTermsReportEntityTest.xml | 2 +- .../ViewedProductsReportEntityTest.php | 3 +- .../ViewedProductsReportEntityTest.xml | 2 +- .../Adminhtml/Customer/Edit/Tab/Reviews.php | 2 +- .../Block/Adminhtml/Edit/CustomerForm.xml | 2 +- .../Block/Adminhtml/Edit/Product/Grid.php | 2 +- .../Block/Adminhtml/Edit/RatingElement.php | 2 +- .../Test/Block/Adminhtml/FormPageActions.php | 2 +- .../Review/Test/Block/Adminhtml/Grid.php | 2 +- .../Product/Edit/Section/Reviews.php | 2 +- .../Test/Block/Adminhtml/Product/Grid.php | 2 +- .../Block/Adminhtml/Product/ProductForm.xml | 2 +- .../Adminhtml/Rating/Edit/RatingForm.php | 2 +- .../Adminhtml/Rating/Edit/RatingForm.xml | 2 +- .../Test/Block/Adminhtml/Rating/Grid.php | 2 +- .../Test/Block/Adminhtml/ReviewForm.php | 2 +- .../Test/Block/Adminhtml/ReviewForm.xml | 2 +- .../Review/Test/Block/Product/View.php | 2 +- .../Test/Block/Product/View/Summary.php | 2 +- .../Magento/Review/Test/Block/ReviewForm.php | 2 +- .../Magento/Review/Test/Block/ReviewForm.xml | 2 +- .../Constraint/AssertProductRatingInGrid.php | 2 +- .../AssertProductRatingInProductPage.php | 2 +- .../AssertProductRatingNotInGrid.php | 2 +- .../AssertProductRatingNotInProductPage.php | 2 +- .../AssertProductRatingOnReviewPage.php | 2 +- ...ssertProductRatingSuccessDeleteMessage.php | 2 +- .../AssertProductRatingSuccessSaveMessage.php | 2 +- ...ProductReviewBackendSuccessSaveMessage.php | 2 +- .../Constraint/AssertProductReviewForm.php | 2 +- .../Constraint/AssertProductReviewInGrid.php | 2 +- ...ssertProductReviewInGridOnCustomerPage.php | 2 +- ...sertProductReviewIsAbsentOnProductPage.php | 2 +- ...ctReviewMassActionSuccessDeleteMessage.php | 2 +- ...tProductReviewMassActionSuccessMessage.php | 2 +- .../AssertProductReviewNotInGrid.php | 2 +- .../AssertProductReviewNotOnProductPage.php | 2 +- .../AssertProductReviewOnProductPage.php | 2 +- .../AssertReviewCreationSuccessMessage.php | 2 +- ...ssertReviewLinksIsPresentOnProductPage.php | 2 +- .../AssertReviewSuccessSaveMessage.php | 2 +- .../AssertSetApprovedProductReview.php | 2 +- .../Magento/Review/Test/Fixture/Rating.xml | 2 +- .../Magento/Review/Test/Fixture/Review.xml | 2 +- .../Review/Test/Fixture/Review/EntityId.php | 2 +- .../Review/Test/Fixture/Review/Ratings.php | 2 +- .../Review/Test/Handler/Rating/Curl.php | 2 +- .../Test/Handler/Rating/RatingInterface.php | 2 +- .../Review/Test/Handler/Review/Curl.php | 2 +- .../Test/Handler/Review/ReviewInterface.php | 2 +- .../Review/Test/Page/Adminhtml/RatingEdit.xml | 2 +- .../Test/Page/Adminhtml/RatingIndex.xml | 2 +- .../Review/Test/Page/Adminhtml/RatingNew.xml | 2 +- .../Review/Test/Page/Adminhtml/ReviewEdit.xml | 2 +- .../Test/Page/Adminhtml/ReviewIndex.xml | 2 +- .../Test/Page/Product/CatalogProductView.xml | 2 +- .../Magento/Review/Test/Repository/Rating.xml | 2 +- .../Magento/Review/Test/Repository/Review.xml | 2 +- .../CreateProductRatingEntityTest.php | 2 +- .../CreateProductRatingEntityTest.xml | 2 +- .../CreateProductReviewBackendEntityTest.php | 3 +- .../CreateProductReviewBackendEntityTest.xml | 2 +- .../CreateProductReviewFrontendEntityTest.php | 2 +- .../CreateProductReviewFrontendEntityTest.xml | 3 +- .../DeleteProductRatingEntityTest.php | 2 +- .../DeleteProductRatingEntityTest.xml | 2 +- ...anageProductReviewFromCustomerPageTest.php | 3 +- ...anageProductReviewFromCustomerPageTest.xml | 2 +- .../MassActionsProductReviewEntityTest.php | 2 +- .../MassActionsProductReviewEntityTest.xml | 2 +- .../ModerateProductReviewEntityTest.php | 2 +- .../ModerateProductReviewEntityTest.xml | 2 +- .../Review/Test/TestCase/NavigateMenuTest.xml | 2 +- ...teProductReviewEntityOnProductPageTest.php | 2 +- ...teProductReviewEntityOnProductPageTest.xml | 3 +- .../UpdateProductReviewEntityTest.php | 2 +- .../UpdateProductReviewEntityTest.xml | 3 +- .../app/Magento/Review/Test/etc/curl/di.xml | 2 +- .../tests/app/Magento/Review/Test/etc/di.xml | 2 +- .../Test/Block/Adminhtml/CreditMemo/Grid.php | 2 +- .../Test/Block/Adminhtml/Invoice/Grid.php | 2 +- .../Block/Adminhtml/Order/AbstractForm.php | 7 +- .../Adminhtml/Order/AbstractForm/Product.php | 2 +- .../Block/Adminhtml/Order/AbstractItems.php | 72 +- .../Adminhtml/Order/AbstractItemsNewBlock.php | 18 +- .../Test/Block/Adminhtml/Order/Actions.php | 29 +- .../Test/Block/Adminhtml/Order/Create.php | 34 +- .../Order/Create/Billing/Address.php | 2 +- .../Order/Create/Billing/Address.xml | 2 +- .../Adminhtml/Order/Create/Billing/Method.php | 65 +- .../Block/Adminhtml/Order/Create/Coupons.php | 2 +- .../Block/Adminhtml/Order/Create/Customer.php | 2 +- .../Order/Create/CustomerActivities.php | 2 +- .../Create/CustomerActivities/Sidebar.php | 24 +- .../Sidebar/LastOrderedItems.php | 2 +- .../Sidebar/ProductsInComparison.php | 2 +- .../Sidebar/RecentlyComparedProducts.php | 2 +- .../Sidebar/RecentlyViewedItems.php | 2 +- .../Sidebar/RecentlyViewedProducts.php | 2 +- .../Sidebar/ShoppingCartItems.php | 26 +- .../CustomerActivities/Sidebar/Wishlist.php | 167 + .../Adminhtml/Order/Create/Form/Account.php | 2 +- .../Adminhtml/Order/Create/Form/Account.xml | 2 +- .../Block/Adminhtml/Order/Create/Items.php | 58 +- .../Order/Create/Items/ItemProduct.php | 2 +- .../Order/Create/Items/ItemProduct.xml | 2 +- .../Adminhtml/Order/Create/Search/Grid.php | 2 +- .../Order/Create/Shipping/Address.php | 47 +- .../Order/Create/Shipping/Address.xml | 27 + .../Order/Create/Shipping/Method.php | 26 +- .../Block/Adminhtml/Order/Create/Sidebar.php | 125 + .../Block/Adminhtml/Order/Create/Store.php | 2 +- .../Block/Adminhtml/Order/Create/Totals.php | 42 +- .../Block/Adminhtml/Order/Creditmemo/Form.php | 2 +- .../Block/Adminhtml/Order/Creditmemo/Form.xml | 2 +- .../Adminhtml/Order/Creditmemo/Form/Items.php | 9 +- .../Order/Creditmemo/Form/Items/Product.php | 2 +- .../Order/Creditmemo/Form/Items/Product.xml | 2 +- .../Block/Adminhtml/Order/Creditmemo/Grid.php | 2 +- .../Adminhtml/Order/Creditmemo/Totals.php | 2 +- .../Adminhtml/Order/Creditmemo/View/Items.php | 2 +- .../Sales/Test/Block/Adminhtml/Order/Grid.php | 2 +- .../Test/Block/Adminhtml/Order/History.php | 121 - .../Block/Adminhtml/Order/Invoice/Form.php | 2 +- .../Block/Adminhtml/Order/Invoice/Form.xml | 2 +- .../Adminhtml/Order/Invoice/Form/Items.php | 14 +- .../Order/Invoice/Form/Items/Product.php | 2 +- .../Order/Invoice/Form/Items/Product.xml | 2 +- .../Block/Adminhtml/Order/Invoice/Grid.php | 2 +- .../Block/Adminhtml/Order/Invoice/Totals.php | 20 +- .../Adminhtml/Order/Invoice/View/Items.php | 2 +- .../Block/Adminhtml/Order/Shipment/Totals.php | 2 +- .../Adminhtml/Order/Shipment/View/Items.php | 12 +- .../Order/Status/Assign/AssignForm.php | 2 +- .../Order/Status/Assign/AssignForm.xml | 2 +- .../Order/Status/GridPageActions.php | 2 +- .../Test/Block/Adminhtml/Order/StatusGrid.php | 2 +- .../Test/Block/Adminhtml/Order/Totals.php | 2 +- .../Adminhtml/Order/Transactions/Grid.php | 2 +- .../Block/Adminhtml/Order/View/Addresses.php | 77 + .../Test/Block/Adminhtml/Order/View/Info.php | 34 +- .../Test/Block/Adminhtml/Order/View/Items.php | 2 +- .../Block/Adminhtml/Order/View/OrderForm.php | 25 +- .../Block/Adminhtml/Order/View/OrderForm.xml | 7 +- .../Adminhtml/Order/View/Tab/CreditMemos.php | 2 +- .../Order/View/Tab/CreditMemos/Grid.php | 40 +- .../Block/Adminhtml/Order/View/Tab/Info.php | 52 +- .../View/Tab/Info/CommentsHistoryBlock.php | 93 + .../Order/View/Tab/Info/PaymentInfoBlock.php | 40 + .../Adminhtml/Order/View/Tab/Invoices.php | 2 +- .../Order/View/Tab/Invoices/Grid.php | 20 +- .../Adminhtml/Order/View/Tab/Shipments.php | 2 +- .../Order/View/Tab/Shipments/Grid.php | 30 +- .../Adminhtml/Order/View/Tab/Transactions.php | 4 +- .../Order/View/Tab/Transactions/Grid.php | 2 +- .../Block/Adminhtml/Report/Filter/Form.php | 2 +- .../Block/Adminhtml/Report/Filter/Form.xml | 2 +- .../Sales/Test/Block/Order/History.php | 21 +- .../Magento/Sales/Test/Block/Order/Info.php | 2 +- .../Magento/Sales/Test/Block/Order/Items.php | 2 +- .../Magento/Sales/Test/Block/Order/View.php | 36 +- .../Test/Block/Order/View/ActionsToolbar.php | 2 +- .../Sales/Test/Block/Widget/Guest/Form.php | 2 +- .../Sales/Test/Block/Widget/Guest/Form.xml | 2 +- .../Test/Constraint/AbstractAssertItems.php | 38 +- .../AbstractAssertOrderOnFrontend.php | 2 +- ...tAcceptPaymentMessageInCommentsHistory.php | 26 +- ...sertAcceptPaymentSuccessMessagePresent.php | 2 +- .../AssertAuthorizationInCommentsHistory.php | 20 +- .../AssertCaptureInCommentsHistory.php | 26 +- ...rtCartSectionIsEmptyOnBackendOrderPage.php | 42 + ...tSectionWithProductsOnBackendOrderPage.php | 47 + .../Constraint/AssertCreditMemoButton.php | 2 +- .../Test/Constraint/AssertCreditMemoItems.php | 2 +- ...ertDenyPaymentMessageInCommentsHistory.php | 24 +- ...AssertDenyPaymentSuccessMessagePresent.php | 2 +- .../AssertInvoiceInInvoicesGrid.php | 2 +- .../Constraint/AssertInvoiceInInvoicesTab.php | 8 +- .../Test/Constraint/AssertInvoiceItems.php | 9 +- .../AssertInvoiceNotInInvoicesGrid.php | 61 + .../AssertInvoiceStatusInOrdersGrid.php | 53 + .../AssertInvoiceSuccessCreateMessage.php | 2 +- ...ssertInvoiceWithShipmentSuccessMessage.php | 2 +- .../AssertInvoicedAmountOnFrontend.php | 2 +- ...ertItemsOrderedSectionContainsProducts.php | 46 + ...emsOrderedSectionOnBackendOrderIsEmpty.php | 46 + .../Constraint/AssertNoCreditMemoButton.php | 2 +- .../Test/Constraint/AssertNoInvoiceButton.php | 2 +- .../AssertOnlineInvoiceCannotBeCreated.php | 59 + .../Test/Constraint/AssertOrderAddresses.php | 62 + ...illingAndShippingAddressesAreDifferent.php | 47 + .../AssertOrderButtonsAvailable.php | 2 +- .../AssertOrderButtonsUnavailable.php | 2 +- ...AssertOrderCancelMassActionFailMessage.php | 2 +- ...ertOrderCancelMassActionSuccessMessage.php | 2 +- .../AssertOrderCancelSuccessMessage.php | 2 +- ...AssertOrderCommentsHistoryNotifyStatus.php | 58 + .../Test/Constraint/AssertOrderGrandTotal.php | 16 +- .../Constraint/AssertOrderInOrdersGrid.php | 2 +- .../AssertOrderInOrdersGridOnFrontend.php | 2 +- ...sertOrderItemsPagerDisplayedOnFrontend.php | 71 + .../AssertOrderItemsPagerHiddenOnFrontend.php | 71 + .../AssertOrderMassOnHoldSuccessMessage.php | 2 +- .../Constraint/AssertOrderNotInOrdersGrid.php | 2 +- .../AssertOrderNotVisibleOnMyAccount.php | 2 +- .../AssertOrderOnHoldFailMessage.php | 2 +- .../AssertOrderOnHoldSuccessMessage.php | 2 +- .../AssertOrderPaymentInformation.php | 55 + .../AssertOrderReleaseFailMessage.php | 2 +- .../AssertOrderReleaseSuccessMessage.php | 2 +- .../AssertOrderStatusDuplicateStatus.php | 2 +- .../Constraint/AssertOrderStatusInGrid.php | 2 +- .../AssertOrderStatusIsCanceled.php | 53 + .../Constraint/AssertOrderStatusIsCorrect.php | 13 +- .../AssertOrderStatusNotAssigned.php | 2 +- .../AssertOrderStatusSuccessAssignMessage.php | 2 +- .../AssertOrderStatusSuccessCreateMessage.php | 2 +- ...ssertOrderStatusSuccessUnassignMessage.php | 2 +- .../AssertOrderSuccessCreateMessage.php | 2 +- .../AssertOrderSuccessVoidedMessage.php | 53 + .../Constraint/AssertOrdersInOrdersGrid.php | 2 +- ...uctInCustomerShoppingCartOnBackendGrid.php | 54 + .../AssertProductInItemsOrderedGrid.php | 2 +- .../Constraint/AssertProductQtyDecreased.php | 105 + ...sertProductQtyDecreasedAfterCreditmemo.php | 110 + .../AssertProductsQtyAfterOrderCancel.php | 128 + .../AssertRefundInCommentsHistory.php | 34 +- .../AssertRefundInCreditMemoTab.php | 4 +- .../Constraint/AssertRefundInRefundsGrid.php | 11 +- .../AssertRefundNotInRefundsGrid.php | 57 + ...sertRefundOrderStatusInCommentsHistory.php | 53 + .../AssertRefundSuccessCreateMessage.php | 2 +- .../AssertRefundedGrandTotalOnFrontend.php | 10 +- ...ertReorderButtonIsNotVisibleOnFrontend.php | 75 + .../AssertReorderStatusIsCorrect.php | 13 +- .../AssertSalesPrintOrderBillingAddress.php | 2 +- .../AssertSalesPrintOrderGrandTotal.php | 2 +- .../AssertSalesPrintOrderPaymentMethod.php | 2 +- .../AssertSalesPrintOrderProducts.php | 2 +- .../Constraint/AssertTransactionStatus.php | 61 + .../Test/Constraint/AssertUnholdButton.php | 2 +- .../AssertVoidInCommentsHistory.php | 61 + .../Sales/Test/Fixture/OrderInjectable.xml | 3 +- .../OrderInjectable/BillingAddressId.php | 2 +- .../Fixture/OrderInjectable/CouponCode.php | 2 +- .../Fixture/OrderInjectable/CustomerId.php | 2 +- .../Test/Fixture/OrderInjectable/EntityId.php | 2 +- .../Test/Fixture/OrderInjectable/StoreId.php | 52 +- .../Sales/Test/Fixture/OrderStatus.xml | 2 +- .../Test/Handler/OrderInjectable/Curl.php | 2 +- .../OrderInjectableInterface.php | 2 +- .../Test/Handler/OrderInjectable/Webapi.php | 50 +- .../Sales/Test/Handler/OrderStatus/Curl.php | 2 +- .../OrderStatus/OrderStatusInterface.php | 2 +- .../Test/Page/Adminhtml/CreditMemoIndex.xml | 2 +- .../Test/Page/Adminhtml/InvoiceIndex.xml | 2 +- .../Test/Page/Adminhtml/OrderCreateIndex.xml | 6 +- .../Page/Adminhtml/OrderCreditMemoNew.xml | 2 +- .../Sales/Test/Page/Adminhtml/OrderIndex.xml | 2 +- .../Test/Page/Adminhtml/OrderInvoiceNew.xml | 2 +- .../Test/Page/Adminhtml/OrderInvoiceView.xml | 2 +- .../Test/Page/Adminhtml/OrderStatusAssign.xml | 2 +- .../Test/Page/Adminhtml/OrderStatusEdit.xml | 2 +- .../Test/Page/Adminhtml/OrderStatusIndex.xml | 2 +- .../Test/Page/Adminhtml/OrderStatusNew.xml | 2 +- .../Page/Adminhtml/SalesCreditMemoView.xml | 2 +- .../Test/Page/Adminhtml/SalesInvoiceView.xml | 2 +- .../Test/Page/Adminhtml/SalesOrderView.xml | 5 +- .../Sales/Test/Page/CreditMemoView.xml | 2 +- .../Sales/Test/Page/CustomerOrderView.xml | 2 +- .../Magento/Sales/Test/Page/InvoiceView.xml | 2 +- .../Magento/Sales/Test/Page/OrderHistory.xml | 2 +- .../Sales/Test/Page/SalesGuestForm.xml | 2 +- .../Sales/Test/Page/SalesGuestPrint.xml | 2 +- .../Sales/Test/Page/SalesGuestView.xml | 2 +- .../Sales/Test/Page/SalesOrderShipmentNew.php | 2 +- .../Sales/Test/Repository/ConfigData.xml | 2 +- .../Sales/Test/Repository/OrderInjectable.xml | 77 +- .../Test/Repository/OrderInjectable/Price.xml | 51 +- .../Sales/Test/Repository/OrderStatus.xml | 2 +- .../TestCase/AssignCustomOrderStatusTest.php | 2 +- .../TestCase/AssignCustomOrderStatusTest.xml | 2 +- .../Test/TestCase/CancelCreatedOrderTest.php | 45 +- .../Test/TestCase/CancelCreatedOrderTest.xml | 52 +- .../Sales/Test/TestCase/CloseOrderTest.php | 45 + .../TestCase/CreateCreditMemoEntityTest.php | 68 +- .../TestCase/CreateCreditMemoEntityTest.xml | 41 +- .../CreateCustomOrderStatusEntityTest.php | 2 +- .../CreateCustomOrderStatusEntityTest.xml | 2 +- .../Test/TestCase/CreateInvoiceEntityTest.php | 34 +- .../Test/TestCase/CreateInvoiceEntityTest.xml | 47 +- .../TestCase/CreateOnlineCreditMemoTest.php | 41 + .../CreateOnlineInvoiceEntityTest.php | 3 +- .../Test/TestCase/CreateOrderBackendTest.php | 4 +- .../Test/TestCase/CreateOrderBackendTest.xml | 42 +- .../CreateOrderFromEditCustomerPageTest.php | 329 + .../CreateOrderFromEditCustomerPageTest.xml | 29 + .../Test/TestCase/FrontendOrderPagerTest.php | 40 + .../Test/TestCase/FrontendOrderPagerTest.xml | 19 + .../Sales/Test/TestCase/GridFilteringTest.xml | 7 +- .../Test/TestCase/GridFullTextSearchTest.xml | 6 +- .../Sales/Test/TestCase/GridSortingTest.xml | 10 +- .../Test/TestCase/HoldCreatedOrderTest.php | 2 +- .../Test/TestCase/HoldCreatedOrderTest.xml | 2 +- .../Test/TestCase/MassOrdersUpdateTest.php | 2 +- .../Test/TestCase/MassOrdersUpdateTest.xml | 4 +- ...MoveLastOrderedProductsOnOrderPageTest.php | 2 +- ...MoveLastOrderedProductsOnOrderPageTest.xml | 4 +- .../MoveProductsInComparedOnOrderPageTest.php | 2 +- .../MoveProductsInComparedOnOrderPageTest.xml | 4 +- ...ecentlyComparedProductsOnOrderPageTest.php | 7 +- ...ecentlyComparedProductsOnOrderPageTest.xml | 9 +- ...eRecentlyViewedProductsOnOrderPageTest.php | 2 +- ...eRecentlyViewedProductsOnOrderPageTest.xml | 4 +- ...oveShoppingCartProductsOnOrderPageTest.php | 2 +- ...oveShoppingCartProductsOnOrderPageTest.xml | 4 +- .../Sales/Test/TestCase/NavigateMenuTest.xml | 2 +- .../TestCase/PrintOrderFrontendGuestTest.php | 2 +- .../TestCase/PrintOrderFrontendGuestTest.xml | 3 +- .../Test/TestCase/ReorderOrderEntityTest.php | 2 +- .../Test/TestCase/ReorderOrderEntityTest.xml | 3 +- .../UnassignCustomOrderStatusTest.php | 2 +- .../UnassignCustomOrderStatusTest.xml | 2 +- .../TestCase/UpdateCustomOrderStatusTest.php | 2 +- .../TestCase/UpdateCustomOrderStatusTest.xml | 2 +- .../Test/TestCase/VoidAuthorizationTest.php | 50 + .../Sales/Test/TestStep/AddProductsStep.php | 38 +- .../AddRecentlyViewedProductsToCartStep.php | 2 +- .../Test/TestStep/ConfigureProductsStep.php | 2 +- .../Test/TestStep/CreateCreditMemoStep.php | 2 +- .../Sales/Test/TestStep/CreateInvoiceStep.php | 2 +- .../Test/TestStep/CreateNewOrderStep.php | 2 +- .../TestStep/CreateOnlineCreditMemoStep.php} | 42 +- .../Sales/Test/TestStep/CreateOrderStep.php | 2 +- .../Test/TestStep/CreateShipmentStep.php | 18 +- .../TestStep/FillAccountInformationStep.php | 2 +- .../Test/TestStep/FillBillingAddressStep.php | 30 +- .../Test/TestStep/FillShippingAddressStep.php | 55 + .../Sales/Test/TestStep/OnHoldStep.php | 2 +- .../Sales/Test/TestStep/OpenOrderStep.php | 2 +- .../OpenSalesOrderOnFrontendForGuestStep.php | 2 +- .../Test/TestStep/OpenSalesOrdersStep.php | 2 +- .../TestStep/PrintOrderOnFrontendStep.php | 2 +- .../Sales/Test/TestStep/ReorderStep.php | 2 +- .../Test/TestStep/SelectCustomerOrderStep.php | 2 +- .../SelectPaymentMethodForOrderStep.php | 18 +- .../SelectShippingMethodForOrderStep.php | 2 +- .../Sales/Test/TestStep/SelectStoreStep.php | 2 +- .../Test/TestStep/SubmitOrderNegativeStep.php | 43 + .../Sales/Test/TestStep/SubmitOrderStep.php | 36 +- .../Test/TestStep/UpdateProductsDataStep.php | 2 +- .../Test/TestStep/VoidAuthorizationStep.php | 63 + .../app/Magento/Sales/Test/etc/curl/di.xml | 2 +- .../tests/app/Magento/Sales/Test/etc/di.xml | 94 +- .../app/Magento/Sales/Test/etc/testcase.xml | 62 +- .../app/Magento/Sales/Test/etc/webapi/di.xml | 2 +- .../Test/Block/Adminhtml/Promo/Grid.php | 2 +- .../Promo/Quote/Edit/PromoQuoteForm.php | 2 +- .../Promo/Quote/Edit/PromoQuoteForm.xml | 7 +- .../Promo/Quote/Edit/Section/Conditions.php | 2 +- .../Promo/Quote/Edit/Section/Labels.php | 2 +- .../Quote/Edit/Section/ManageCouponCode.php | 50 + .../Quote/Edit/Section/RuleInformation.php | 2 +- .../SalesRule/Test/Block/Order/Items.php | 2 +- .../SalesRule/Test/Block/Order/View.php | 2 +- .../AssertCartPriceRuleApplying.php | 16 +- .../AssertCartPriceRuleConditionIsApplied.php | 2 +- ...sertCartPriceRuleConditionIsNotApplied.php | 2 +- .../Constraint/AssertCartPriceRuleForm.php | 4 +- ...sertCartPriceRuleFreeShippingIsApplied.php | 2 +- ...ssertCartPriceRuleIsNotPresentedInGrid.php | 2 +- ...ssertCartPriceRuleSuccessDeleteMessage.php | 2 +- .../AssertCartPriceRuleSuccessSaveMessage.php | 2 +- .../AssertSalesRuleOnPrintOrder.php | 2 +- .../SalesRule/Test/Fixture/SalesRule.xml | 2 +- .../SalesRule/ConditionsSerialized.php | 2 +- .../SalesRule/Test/Handler/SalesRule/Curl.php | 6 +- .../Handler/SalesRule/SalesRuleInterface.php | 2 +- .../Test/Handler/SalesRule/Webapi.php | 2 +- .../Test/Page/Adminhtml/PromoQuoteEdit.xml | 2 +- .../Test/Page/Adminhtml/PromoQuoteIndex.xml | 2 +- .../Test/Page/Adminhtml/PromoQuoteNew.xml | 2 +- .../SalesRule/Test/Page/SalesGuestPrint.xml | 2 +- .../SalesRule/Test/Repository/SalesRule.xml | 26 +- .../ApplySeveralSalesRuleEntityTest.php | 2 +- .../ApplySeveralSalesRuleEntityTest.xml | 5 +- .../TestCase/CreateSalesRuleEntityTest.php | 23 +- .../TestCase/CreateSalesRuleEntityTest.xml | 55 +- .../TestCase/DeleteSalesRuleEntityTest.php | 11 +- .../TestCase/DeleteSalesRuleEntityTest.xml | 7 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../OnePageCheckoutWithDiscountTest.php | 2 +- .../ShoppingCartWithFreeShippingTest.php | 77 + .../ShoppingCartWithFreeShippingTest.xml | 25 + .../TestCase/UpdateSalesRuleEntityTest.php | 2 +- .../TestCase/UpdateSalesRuleEntityTest.xml | 2 +- .../TestStep/ApplySalesRuleOnBackendStep.php | 2 +- .../TestStep/ApplySalesRuleOnCheckoutStep.php | 2 +- .../TestStep/ApplySalesRuleOnFrontendStep.php | 2 +- .../Test/TestStep/CreateSalesRuleStep.php | 2 +- .../Test/TestStep/DeleteAllSalesRuleStep.php | 2 +- .../Magento/SalesRule/Test/etc/curl/di.xml | 2 +- .../Magento/SalesRule/Test/etc/testcase.xml | 6 +- .../Magento/SalesRule/Test/etc/webapi/di.xml | 2 +- .../Adminhtml/Block/Edit/SynonymGroupForm.php | 2 +- .../Adminhtml/Block/Edit/SynonymGroupForm.xml | 2 +- .../Adminhtml/Block/SynonymGroupGrid.php | 9 +- .../AssertSynonymGroupDeleteMessage.php | 2 +- .../Constraint/AssertSynonymGroupInGrid.php | 58 +- .../AssertSynonymGroupSuccessSaveMessage.php | 2 +- .../Constraint/AssertSynonymGroupsSearch.php | 59 + .../AssertSynonymMergeErrorMessage.php | 2 +- .../AssertSynonymRestrictedAccess.php | 50 + .../Search/Test/Fixture/SynonymGroup.xml | 2 +- .../Test/Fixture/SynonymGroup/ScopeId.php | 2 +- .../Search/Test/Handler/SynonymGroup/Curl.php | 2 +- .../SynonymGroup/SynonymGroupInterface.php | 2 +- .../Test/Page/Adminhtml/SynonymGroupIndex.xml | 2 +- .../Test/Page/Adminhtml/SynonymGroupNew.xml | 2 +- .../Search/Test/Repository/SynonymGroup.xml | 26 +- .../AdvancedSearchWithAttributeTest.php | 391 + .../AdvancedSearchWithAttributeTest.xml | 38 + .../CreateMultipleSynonymGroupsTest.php | 73 + .../CreateMultipleSynonymGroupsTest.xml | 60 + .../TestCase/CreateSynonymGroupEntityTest.php | 2 +- .../TestCase/CreateSynonymGroupEntityTest.xml | 4 +- .../Test/TestCase/CustomAclPermissionTest.xml | 17 + .../TestCase/DeleteSynonymGroupEntityTest.php | 2 +- .../TestCase/DeleteSynonymGroupEntityTest.xml | 2 +- .../TestCase/MergeSynonymGroupEntityTest.php | 2 +- .../TestCase/MergeSynonymGroupEntityTest.xml | 2 +- .../TestCase/UpdateSynonymGroupEntityTest.php | 2 +- .../TestCase/UpdateSynonymGroupEntityTest.xml | 4 +- .../TestStep/DeleteAllSynonymGroupsStep.php | 43 + .../app/Magento/Search/Test/etc/curl/di.xml | 2 +- .../tests/app/Magento/Search/Test/etc/di.xml | 2 +- .../Test/Block/Form/ForgotPassword.php | 2 +- .../Test/Block/Form/ForgotPassword.xml | 4 +- .../Constraint/AssertCustomerEmailChanged.php | 64 + .../Constraint/AssertCustomerIsLocked.php | 4 +- .../AssertCustomerPasswordRequiredClasses.php | 52 + .../AssertCustomerResetPasswordFailed.php | 2 +- .../AssertDefaultAccountInformation.php | 58 + ...AssertPasswordIsNotSecureEnoughMessage.php | 2 +- .../AssertPasswordLengthErrorMessage.php | 2 +- .../Test/Constraint/AssertUserIsLocked.php | 2 +- .../AssertUserPasswordResetFailed.php | 2 +- .../Test/Page/UserAccountForgotPassword.php | 2 +- .../Security/Test/Repository/ConfigData.xml | 10 +- ...dminUserWhenCreatingNewIntegrationTest.php | 2 +- ...dminUserWhenCreatingNewIntegrationTest.xml | 2 +- .../LockAdminUserWhenCreatingNewRoleTest.php | 2 +- .../LockAdminUserWhenCreatingNewRoleTest.xml | 2 +- .../LockAdminUserWhenCreatingNewUserTest.php | 134 + .../LockAdminUserWhenCreatingNewUserTest.xml | 25 + ...ockAdminUserWhenEditingIntegrationTest.php | 143 + ...ockAdminUserWhenEditingIntegrationTest.xml | 21 + .../LockAdminUserWhenEditingRoleTest.php | 140 + .../LockAdminUserWhenEditingRoleTest.xml | 22 + .../LockAdminUserWhenEditingUserTest.php | 133 + .../LockAdminUserWhenEditingUserTest.xml | 25 + .../TestCase/LockCustomerOnEditPageTest.php | 2 +- .../TestCase/LockCustomerOnEditPageTest.xml | 6 +- .../TestCase/LockCustomerOnLoginPageTest.php | 2 +- .../TestCase/LockCustomerOnLoginPageTest.xml | 4 +- .../NewCustomerPasswordComplexityTest.php | 2 +- .../NewCustomerPasswordComplexityTest.xml | 2 +- ...EntityWithDifferentPasswordClassesTest.php | 111 + ...EntityWithDifferentPasswordClassesTest.xml | 95 + .../ResetCustomerPasswordFailedTest.php | 2 +- .../ResetCustomerPasswordFailedTest.xml | 2 +- .../TestCase/ResetUserPasswordFailedTest.php | 2 +- .../TestCase/ResetUserPasswordFailedTest.xml | 2 +- .../SecureChangingCustomerEmailTest.php | 88 + .../SecureChangingCustomerEmailTest.xml | 26 + .../SecureChangingCustomerPasswordTest.php | 91 + .../SecureChangingCustomerPasswordTest.xml | 36 + .../app/Magento/Security/Test/etc/di.xml | 2 +- .../Setup/Test/Block/Authentication.php | 2 +- .../Setup/Test/Block/Authentication.xml | 2 +- .../Magento/Setup/Test/Block/CreateBackup.php | 2 +- .../Magento/Setup/Test/Block/CreateBackup.xml | 2 +- .../Test/Block/Extension/AbstractGrid.php | 2 +- .../Setup/Test/Block/Extension/DataOption.php | 2 +- .../Setup/Test/Block/Extension/Grid.php | 2 +- .../Test/Block/Extension/InstallGrid.php | 2 +- .../Setup/Test/Block/Extension/UpdateGrid.php | 2 +- .../Setup/Test/Block/Extension/Updater.php | 2 +- .../app/Magento/Setup/Test/Block/Home.php | 2 +- .../Magento/Setup/Test/Block/Module/Grid.php | 2 +- .../Setup/Test/Block/Module/Status.php | 2 +- .../Magento/Setup/Test/Block/Readiness.php | 2 +- .../Setup/Test/Block/SelectVersion.php | 2 +- .../Setup/Test/Block/SelectVersion.xml | 2 +- .../Setup/Test/Block/SuccessMessage.php | 2 +- .../Magento/Setup/Test/Block/SystemConfig.php | 2 +- .../Setup/Test/Block/SystemUpgrade.php | 2 +- .../Constraint/AssertApplicationVersion.php | 2 +- .../Test/Constraint/AssertSuccessMessage.php | 2 +- .../AssertSuccessfulReadinessCheck.php | 2 +- .../AssertVersionAndEditionCheck.php | 2 +- .../AssertExtensionAndVersionCheck.php | 2 +- .../Extension/AssertFindExtensionOnGrid.php | 2 +- ...AssertMultipleExtensionAndVersionCheck.php | 2 +- .../AssertMultipleSuccessMessage.php | 2 +- .../AssertSelectSeveralExtensions.php | 2 +- .../Extension/AssertSuccessMessage.php | 2 +- .../Extension/AssertVersionOnGrid.php | 2 +- .../Constraint/Module/AssertModuleInGrid.php | 2 +- .../Module/AssertSuccessMessage.php | 2 +- .../Setup/Test/Fixture/BackupOptions.xml | 2 +- .../Magento/Setup/Test/Fixture/Extension.xml | 2 +- .../app/Magento/Setup/Test/Fixture/Module.xml | 2 +- .../Setup/Test/Fixture/RepoCredentials.xml | 2 +- .../Magento/Setup/Test/Fixture/Upgrade.xml | 2 +- .../Setup/Test/Page/Adminhtml/SetupWizard.xml | 2 +- .../Setup/Test/Repository/BackupOptions.xml | 2 +- .../Setup/Test/Repository/Extension.xml | 2 +- .../Setup/Test/Repository/RepoCredentials.xml | 2 +- .../Test/TestCase/AbstractExtensionTest.php | 2 +- .../Test/TestCase/EnableDisableModuleTest.php | 2 +- .../Test/TestCase/EnableDisableModuleTest.xml | 2 +- .../Test/TestCase/ExtensionMultipleTest.php | 2 +- .../Test/TestCase/ExtensionMultipleTest.xml | 2 +- .../TestCase/ExtensionMultipleUpdateTest.php | 2 +- .../TestCase/ExtensionMultipleUpdateTest.xml | 2 +- .../Setup/Test/TestCase/ExtensionTest.php | 2 +- .../Setup/Test/TestCase/ExtensionTest.xml | 2 +- .../Setup/Test/TestCase/UpgradeSystemTest.php | 4 +- .../Setup/Test/TestCase/UpgradeSystemTest.xml | 2 +- .../Shipping/Test/Block/Adminhtml/Form.php | 4 +- .../Shipping/Test/Block/Adminhtml/Form.xml | 2 +- .../Test/Block/Adminhtml/Form/Items.php | 9 +- .../Block/Adminhtml/Form/Items/Product.php | 2 +- .../Block/Adminhtml/Form/Items/Product.xml | 2 +- .../Test/Block/Adminhtml/Order/Tracking.php | 2 +- .../Block/Adminhtml/Order/Tracking/Item.php | 2 +- .../Block/Adminhtml/Order/Tracking/Item.xml | 2 +- .../Test/Block/Adminhtml/Shipment/Grid.php | 6 +- .../Test/Block/Adminhtml/View/Items.php | 2 +- .../Shipping/Test/Block/Order/Info.php | 2 +- .../Shipping/Test/Block/Order/Shipment.php | 2 +- .../Test/Block/Order/Shipment/Items.php | 2 +- .../AssertCityBasedShippingRateChanged.php | 50 + .../Test/Constraint/AssertNoShipButton.php | 2 +- .../Constraint/AssertShipTotalQuantity.php | 2 +- .../AssertShipmentInShipmentsGrid.php | 2 +- .../AssertShipmentInShipmentsTab.php | 8 +- .../Test/Constraint/AssertShipmentItems.php | 2 +- .../AssertShipmentNotInShipmentsGrid.php | 57 + .../AssertShipmentSuccessCreateMessage.php | 2 +- .../AssertShippingMethodOnPrintOrder.php | 2 +- .../Magento/Shipping/Test/Fixture/Method.php | 2 +- .../Test/Page/Adminhtml/OrderShipmentNew.xml | 2 +- .../Test/Page/Adminhtml/OrderShipmentView.xml | 2 +- .../Test/Page/Adminhtml/SalesShipmentView.xml | 2 +- .../Test/Page/Adminhtml/ShipmentIndex.xml | 2 +- .../Shipping/Test/Page/SalesGuestPrint.xml | 2 +- .../Shipping/Test/Page/ShipmentView.xml | 2 +- .../Shipping/Test/Repository/ConfigData.xml | 2 +- .../Shipping/Test/Repository/Method.php | 2 +- .../TestCase/CityBasedShippingRateTest.php | 44 + .../TestCase/CreateShipmentEntityTest.php | 2 +- .../TestCase/CreateShipmentEntityTest.xml | 2 +- .../SalesShippingReportEntityTest.php | 64 + .../SalesShippingReportEntityTest.xml | 48 + .../TestStep/FillShippingAddressesStep.php | 98 + .../app/Magento/Shipping/Test/etc/di.xml | 14 + .../Magento/Shipping/Test/etc/testcase.xml | 18 + .../Test/Block/Adminhtml/SitemapGrid.php | 2 +- .../Block/Adminhtml/SitemapPageActions.php | 2 +- .../Test/Constraint/AssertSitemapContent.php | 2 +- .../AssertSitemapFailFolderSaveMessage.php | 2 +- .../AssertSitemapFailPathSaveMessage.php | 2 +- .../Test/Constraint/AssertSitemapInGrid.php | 2 +- .../Constraint/AssertSitemapNotInGrid.php | 2 +- .../AssertSitemapSubmissionToRobotsTxt.php | 60 + .../AssertSitemapSuccessDeleteMessage.php | 2 +- .../AssertSitemapSuccessGenerateMessage.php | 2 +- ...tSitemapSuccessSaveAndGenerateMessages.php | 2 +- .../AssertSitemapSuccessSaveMessage.php | 2 +- .../Magento/Sitemap/Test/Fixture/Sitemap.xml | 2 +- .../Sitemap/Test/Handler/Sitemap/Curl.php | 2 +- .../Test/Handler/Sitemap/SitemapInterface.php | 2 +- .../Test/Page/Adminhtml/SitemapEdit.xml | 2 +- .../Test/Page/Adminhtml/SitemapIndex.xml | 2 +- .../Test/Page/Adminhtml/SitemapNew.xml | 2 +- .../Sitemap/Test/Repository/ConfigData.xml | 27 + .../Sitemap/Test/Repository/Sitemap.xml | 2 +- .../Test/TestCase/CreateSitemapEntityTest.php | 2 +- .../Test/TestCase/CreateSitemapEntityTest.xml | 2 +- .../Test/TestCase/DeleteSitemapEntityTest.php | 2 +- .../Test/TestCase/DeleteSitemapEntityTest.xml | 2 +- .../TestCase/GenerateSitemapEntityTest.php | 143 + .../TestCase/GenerateSitemapEntityTest.xml | 32 + .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../Test/TestCase/UpdateSitemapEntityTest.php | 89 + .../Test/TestCase/UpdateSitemapEntityTest.xml | 20 + .../app/Magento/Sitemap/Test/etc/curl/di.xml | 4 +- .../tests/app/Magento/Sitemap/Test/etc/di.xml | 2 +- .../app/Magento/Store/Test/Block/Switcher.php | 2 +- .../Test/Constraint/AssertStoreBackend.php | 2 +- .../Test/Constraint/AssertStoreCodeInUrl.php | 46 + .../AssertStoreDisabledErrorSaveMessage.php | 46 + .../Store/Test/Constraint/AssertStoreForm.php | 2 +- .../Test/Constraint/AssertStoreFrontend.php | 2 +- .../Test/Constraint/AssertStoreGroupForm.php | 2 +- .../Constraint/AssertStoreGroupInGrid.php | 2 +- .../AssertStoreGroupNoDeleteButton.php | 40 + .../Constraint/AssertStoreGroupNotInGrid.php | 2 +- .../AssertStoreGroupOnStoreViewForm.php | 2 +- ...oreGroupSuccessDeleteAndBackupMessages.php | 2 +- .../AssertStoreGroupSuccessDeleteMessage.php | 2 +- .../AssertStoreGroupSuccessSaveMessage.php | 2 +- .../Test/Constraint/AssertStoreInGrid.php | 2 +- .../Constraint/AssertStoreNoDeleteButton.php | 40 + .../Test/Constraint/AssertStoreNotInGrid.php | 2 +- .../Constraint/AssertStoreNotOnFrontend.php | 2 +- ...ertStoreSuccessDeleteAndBackupMessages.php | 2 +- .../AssertStoreSuccessDeleteMessage.php | 2 +- .../AssertStoreSuccessSaveMessage.php | 2 +- .../Test/Constraint/AssertWebsiteForm.php | 2 +- .../Test/Constraint/AssertWebsiteInGrid.php | 2 +- .../Constraint/AssertWebsiteNotInGrid.php | 2 +- .../Constraint/AssertWebsiteOnStoreForm.php | 2 +- ...tWebsiteSuccessDeleteAndBackupMessages.php | 2 +- .../AssertWebsiteSuccessDeleteMessage.php | 2 +- .../AssertWebsiteSuccessSaveMessage.php | 2 +- .../app/Magento/Store/Test/Fixture/Store.xml | 2 +- .../Store/Test/Fixture/Store/GroupId.php | 13 +- .../Magento/Store/Test/Fixture/StoreGroup.xml | 2 +- .../Test/Fixture/StoreGroup/CategoryId.php | 11 +- .../Test/Fixture/StoreGroup/WebsiteId.php | 10 +- .../Magento/Store/Test/Fixture/Website.xml | 2 +- .../Magento/Store/Test/Handler/Store/Curl.php | 2 +- .../Test/Handler/Store/StoreInterface.php | 2 +- .../Store/Test/Handler/StoreGroup/Curl.php | 2 +- .../StoreGroup/StoreGroupInterface.php | 2 +- .../Store/Test/Handler/Website/Curl.php | 2 +- .../Test/Handler/Website/WebsiteInterface.php | 2 +- .../Store/Test/Repository/ConfigData.xml | 28 + .../Magento/Store/Test/Repository/Store.xml | 30 +- .../Store/Test/Repository/StoreGroup.xml | 22 +- .../Magento/Store/Test/Repository/Website.xml | 12 +- .../AccessAdminWithStoreCodeInUrlTest.php | 82 + .../AccessAdminWithStoreCodeInUrlTest.xml | 18 + .../Test/TestCase/CreateStoreEntityTest.php | 3 +- .../Test/TestCase/CreateStoreEntityTest.xml | 24 +- .../TestCase/CreateStoreGroupEntityTest.php | 3 +- .../TestCase/CreateStoreGroupEntityTest.xml | 11 +- .../Test/TestCase/CreateWebsiteEntityTest.php | 3 +- .../Test/TestCase/CreateWebsiteEntityTest.xml | 3 +- .../Test/TestCase/DeleteStoreEntityTest.php | 3 +- .../Test/TestCase/DeleteStoreEntityTest.xml | 5 +- .../TestCase/DeleteStoreGroupEntityTest.php | 3 +- .../TestCase/DeleteStoreGroupEntityTest.xml | 4 +- .../Test/TestCase/DeleteWebsiteEntityTest.php | 4 +- .../Test/TestCase/DeleteWebsiteEntityTest.xml | 4 +- .../MoveStoreToOtherGroupSameWebsiteTest.php | 107 + .../MoveStoreToOtherGroupSameWebsiteTest.xml | 20 + .../Test/TestCase/UpdateStoreEntityTest.php | 3 +- .../Test/TestCase/UpdateStoreEntityTest.xml | 3 +- .../TestCase/UpdateStoreGroupEntityTest.php | 3 +- .../TestCase/UpdateStoreGroupEntityTest.xml | 5 +- .../Test/TestCase/UpdateWebsiteEntityTest.php | 3 +- .../Test/TestCase/UpdateWebsiteEntityTest.xml | 3 +- .../app/Magento/Store/Test/etc/curl/di.xml | 2 +- .../tests/app/Magento/Store/Test/etc/di.xml | 124 + .../Constraint/AssertApiInfoTitleOnPage.php | 2 +- .../AssertEndpointContentDisplay.php | 2 +- .../AssertServiceContentDisplay.php | 2 +- .../AssertSwaggerSectionLoadOnPage.php | 2 +- .../Swagger/Test/Page/SwaggerUiPage.php | 2 +- .../Test/TestCase/SwaggerUiForRestApiTest.php | 2 +- .../Test/TestCase/SwaggerUiForRestApiTest.xml | 2 +- .../Test/Block/Product/ListProduct.php | 30 + .../Block/Product/ProductList/ProductItem.php | 73 + .../Test/Block/Product/ViewWithSwatches.php | 45 + .../AssertSwatchConfigurableProductPage.php | 92 + .../Swatches/Test/Fixture/Cart/Item.php | 17 + .../Test/Fixture/ConfigurableProduct.xml | 16 + .../Test/Fixture/SwatchProductAttribute.xml | 16 + .../Handler/SwatchProductAttribute/Curl.php | 49 + .../SwatchProductAttributeInterface.php | 17 + .../Page/Category/CatalogCategoryView.xml | 12 + .../Test/Page/Product/CatalogProductView.xml | 12 + .../Test/Repository/ConfigurableProduct.xml | 83 + .../ConfigurableProduct/CheckoutData.xml | 51 + .../ConfigurableAttributesData.xml | 151 + .../Repository/SwatchProductAttribute.xml | 35 + ...bleProductWithSwatchToShoppingCartTest.php | 83 + ...bleProductWithSwatchToShoppingCartTest.xml | 23 + ...oductToCartFromCatalogCategoryPageStep.php | 96 + .../app/Magento/Swatches/Test/etc/curl/di.xml | 10 + .../Test/Block/Adminhtml/Rate/Edit/Form.php | 2 +- .../Test/Block/Adminhtml/Rate/Edit/Form.xml | 2 +- .../Adminhtml/Rate/Edit/FormPageActions.php | 2 +- .../Tax/Test/Block/Adminhtml/Rate/Grid.php | 2 +- .../Block/Adminhtml/Rate/GridPageActions.php | 2 +- .../Test/Block/Adminhtml/Rule/Edit/Form.php | 2 +- .../Test/Block/Adminhtml/Rule/Edit/Form.xml | 2 +- .../Block/Adminhtml/Rule/Edit/TaxRate.php | 2 +- .../Block/Adminhtml/Rule/Edit/TaxRate.xml | 2 +- .../Tax/Test/Block/Adminhtml/Rule/Grid.php | 2 +- .../AbstractAssertOrderTaxOnBackend.php | 2 +- ...tractAssertTaxCalculationAfterCheckout.php | 2 +- ...tractAssertTaxRuleIsAppliedToAllPrices.php | 2 +- ...stractAssertTaxWithCrossBorderApplying.php | 2 +- ...OrderTaxOnBackendExcludingIncludingTax.php | 2 +- .../AssertOrderTaxOnBackendExcludingTax.php | 2 +- .../AssertOrderTaxOnBackendIncludingTax.php | 2 +- ...tionAfterCheckoutExcludingIncludingTax.php | 2 +- ...axCalculationAfterCheckoutExcludingTax.php | 2 +- ...axCalculationAfterCheckoutIncludingTax.php | 2 +- .../Tax/Test/Constraint/AssertTaxRateForm.php | 2 +- .../Test/Constraint/AssertTaxRateInGrid.php | 2 +- .../Constraint/AssertTaxRateInTaxRule.php | 2 +- .../Constraint/AssertTaxRateNotInGrid.php | 2 +- .../Constraint/AssertTaxRateNotInTaxRule.php | 2 +- .../AssertTaxRateSuccessDeleteMessage.php | 2 +- .../AssertTaxRateSuccessSaveMessage.php | 2 +- .../Test/Constraint/AssertTaxRuleApplying.php | 2 +- .../Tax/Test/Constraint/AssertTaxRuleForm.php | 2 +- .../Test/Constraint/AssertTaxRuleInGrid.php | 2 +- .../Constraint/AssertTaxRuleIsApplied.php | 2 +- ...ppliedToAllPricesExcludingIncludingTax.php | 2 +- ...axRuleIsAppliedToAllPricesExcludingTax.php | 2 +- ...axRuleIsAppliedToAllPricesIncludingTax.php | 2 +- .../Constraint/AssertTaxRuleIsNotApplied.php | 2 +- .../Constraint/AssertTaxRuleNotInGrid.php | 2 +- .../AssertTaxRuleSuccessDeleteMessage.php | 2 +- .../AssertTaxRuleSuccessSaveMessage.php | 2 +- .../AssertTaxWithCrossBorderApplied.php | 2 +- .../AssertTaxWithCrossBorderNotApplied.php | 2 +- .../app/Magento/Tax/Test/Fixture/TaxClass.xml | 2 +- .../app/Magento/Tax/Test/Fixture/TaxRate.xml | 2 +- .../app/Magento/Tax/Test/Fixture/TaxRule.xml | 2 +- .../Tax/Test/Fixture/TaxRule/TaxClass.php | 2 +- .../Tax/Test/Fixture/TaxRule/TaxRate.php | 2 +- .../Tax/Test/Handler/Curl/RemoveTaxRule.php | 6 +- .../Tax/Test/Handler/TaxClass/Curl.php | 2 +- .../Handler/TaxClass/TaxClassInterface.php | 2 +- .../Tax/Test/Handler/TaxClass/Webapi.php | 2 +- .../Magento/Tax/Test/Handler/TaxRate/Curl.php | 2 +- .../Test/Handler/TaxRate/TaxRateInterface.php | 2 +- .../Tax/Test/Handler/TaxRate/Webapi.php | 2 +- .../Magento/Tax/Test/Handler/TaxRule/Curl.php | 2 +- .../Test/Handler/TaxRule/TaxRuleInterface.php | 2 +- .../Tax/Test/Handler/TaxRule/Webapi.php | 2 +- .../Tax/Test/Page/Adminhtml/TaxRateIndex.xml | 2 +- .../Tax/Test/Page/Adminhtml/TaxRateNew.xml | 2 +- .../Tax/Test/Page/Adminhtml/TaxRuleIndex.xml | 2 +- .../Tax/Test/Page/Adminhtml/TaxRuleNew.xml | 2 +- .../Tax/Test/Repository/ConfigData.xml | 58 +- .../Magento/Tax/Test/Repository/TaxClass.xml | 2 +- .../Magento/Tax/Test/Repository/TaxRate.xml | 2 +- .../Magento/Tax/Test/Repository/TaxRule.xml | 2 +- .../TestCase/ApplyTaxBasedOnVatIdTest.php | 2 +- .../TestCase/ApplyTaxBasedOnVatIdTest.xml | 2 +- .../Test/TestCase/CreateTaxRateEntityTest.php | 2 +- .../Test/TestCase/CreateTaxRateEntityTest.xml | 2 +- .../Test/TestCase/CreateTaxRuleEntityTest.php | 3 +- .../Test/TestCase/CreateTaxRuleEntityTest.xml | 6 +- .../Test/TestCase/DeleteTaxRateEntityTest.php | 2 +- .../Test/TestCase/DeleteTaxRateEntityTest.xml | 2 +- .../Test/TestCase/DeleteTaxRuleEntityTest.php | 2 +- .../Test/TestCase/DeleteTaxRuleEntityTest.xml | 2 +- .../Tax/Test/TestCase/NavigateMenuTest.xml | 2 +- .../Tax/Test/TestCase/TaxCalculationTest.php | 2 +- .../Tax/Test/TestCase/TaxCalculationTest.xml | 8 +- .../Test/TestCase/TaxWithCrossBorderTest.php | 3 +- .../Test/TestCase/TaxWithCrossBorderTest.xml | 2 +- .../Test/TestCase/UpdateTaxRateEntityTest.php | 2 +- .../Test/TestCase/UpdateTaxRateEntityTest.xml | 2 +- .../Test/TestCase/UpdateTaxRuleEntityTest.php | 2 +- .../Test/TestCase/UpdateTaxRuleEntityTest.xml | 3 +- .../Tax/Test/TestStep/CreateTaxRuleStep.php | 2 +- .../Test/TestStep/DeleteAllTaxRulesStep.php | 2 +- .../app/Magento/Tax/Test/etc/curl/di.xml | 2 +- .../tests/app/Magento/Tax/Test/etc/di.xml | 2 +- .../app/Magento/Tax/Test/etc/testcase.xml | 2 +- .../app/Magento/Tax/Test/etc/webapi/di.xml | 2 +- .../Theme/Test/Block/Html/Breadcrumbs.php | 2 +- .../Magento/Theme/Test/Block/Html/Footer.php | 2 +- .../Magento/Theme/Test/Block/Html/Logo.php | 25 + .../Magento/Theme/Test/Block/Html/Title.php | 2 +- .../Magento/Theme/Test/Block/Html/Topmenu.php | 22 +- .../app/Magento/Theme/Test/Block/Links.php | 13 +- .../Test/Page/CheckoutOnepageSuccess.xml | 2 +- .../Theme/Test/TestCase/NavigateMenuTest.xml | 2 +- .../Block/Adminhtml/AbstractContainer.php | 2 +- .../Adminhtml/AbstractFormContainers.php | 4 +- .../Ui/Test/Block/Adminhtml/DataGrid.php | 73 +- .../Ui/Test/Block/Adminhtml/FormSections.php | 2 +- .../Magento/Ui/Test/Block/Adminhtml/Modal.php | 46 +- .../Ui/Test/Block/Adminhtml/Section.php | 2 +- .../app/Magento/Ui/Test/Block/Messages.php | 161 + .../Test/Constraint/AssertGridFiltering.php | 2 +- .../Constraint/AssertGridFullTextSearch.php | 2 +- .../Ui/Test/Constraint/AssertGridSorting.php | 2 +- .../Ui/Test/TestCase/GridFilteringTest.php | 4 +- .../Test/TestCase/GridFullTextSearchTest.php | 4 +- .../Ui/Test/TestCase/GridSortingTest.php | 3 +- .../tests/app/Magento/Ui/Test/etc/di.xml | 24 + .../Ups/Test/Repository/ConfigData.xml | 2 +- .../Ups/Test/TestCase/OnePageCheckoutTest.xml | 6 +- .../Block/Adminhtml/Catalog/Category/Grid.php | 15 +- .../Block/Adminhtml/Catalog/Category/Tree.php | 2 +- .../Adminhtml/Catalog/Edit/UrlRewriteForm.php | 2 +- .../Adminhtml/Catalog/Edit/UrlRewriteForm.xml | 2 +- .../Block/Adminhtml/Catalog/Product/Grid.php | 2 +- .../Test/Block/Adminhtml/Cms/Page/Grid.php | 2 +- .../Test/Block/Adminhtml/Selector.php | 2 +- .../AssertCategoryUrlWithCustomStoreView.php | 62 + .../AssertPageByUrlRewriteIsNotFound.php | 2 +- .../AssertUrlRewriteAfterDeletingCategory.php | 61 + .../AssertUrlRewriteCategoryInGrid.php | 163 +- .../AssertUrlRewriteCategoryNotInGrid.php | 2 +- .../AssertUrlRewriteCategoryRedirect.php | 2 +- .../AssertUrlRewriteCustomRedirect.php | 2 +- .../AssertUrlRewriteCustomSearchRedirect.php | 2 +- .../AssertUrlRewriteDeletedMessage.php | 2 +- .../Constraint/AssertUrlRewriteInGrid.php | 2 +- .../Constraint/AssertUrlRewriteNotInGrid.php | 2 +- .../AssertUrlRewriteProductInGrid.php | 165 + .../AssertUrlRewriteProductNotInGrid.php | 45 + .../AssertUrlRewriteProductRedirect.php | 2 +- .../AssertUrlRewriteRedirectInGrid.php | 97 + .../AssertUrlRewriteSaveMessage.php | 2 +- ...AssertUrlRewriteSuccessOutsideRedirect.php | 2 +- .../AssertUrlRewriteUpdatedProductInGrid.php | 2 +- .../AssertUrlRewritesCategoriesInGrid.php | 60 + .../AssertUrlRewritesRedirectInGrid.php | 54 + .../UrlRewrite/Test/Fixture/UrlRewrite.xml | 2 +- .../Test/Fixture/UrlRewrite/StoreId.php | 2 +- .../Test/Fixture/UrlRewrite/TargetPath.php | 2 +- .../Test/Handler/UrlRewrite/Curl.php | 2 +- .../UrlRewrite/UrlRewriteInterface.php | 2 +- .../Test/Page/Adminhtml/UrlRewriteEdit.xml | 2 +- .../Test/Page/Adminhtml/UrlRewriteIndex.xml | 2 +- .../UrlRewrite/Test/Repository/UrlRewrite.xml | 2 +- .../Test/TestCase/CategoryUrlRewriteTest.php | 102 + .../Test/TestCase/CategoryUrlRewriteTest.xml | 21 + .../CreateCategoryRewriteEntityTest.php | 2 +- .../CreateCategoryRewriteEntityTest.xml | 2 +- .../CreateCustomUrlRewriteEntityTest.php | 2 +- .../CreateCustomUrlRewriteEntityTest.xml | 2 +- .../CreateProductUrlRewriteEntityTest.php | 2 +- .../CreateProductUrlRewriteEntityTest.xml | 12 +- ...oductWithSeveralWebsitesUrlRewriteTest.php | 104 + ...oductWithSeveralWebsitesUrlRewriteTest.xml | 26 + .../DeleteCategoryUrlRewriteEntityTest.php | 2 +- .../DeleteCategoryUrlRewriteEntityTest.xml | 2 +- .../DeleteCustomUrlRewriteEntityTest.php | 2 +- .../DeleteCustomUrlRewriteEntityTest.xml | 2 +- .../DeleteProductUrlRewriteEntityTest.php | 2 +- .../DeleteProductUrlRewriteEntityTest.xml | 2 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../UpdateCategoryUrlRewriteEntityTest.php | 2 +- .../UpdateCategoryUrlRewriteEntityTest.xml | 2 +- .../UpdateCustomUrlRewriteEntityTest.php | 2 +- .../UpdateCustomUrlRewriteEntityTest.xml | 2 +- .../UpdateProductUrlRewriteEntityTest.php | 2 +- .../UpdateProductUrlRewriteEntityTest.xml | 2 +- .../Magento/UrlRewrite/Test/etc/curl/di.xml | 2 +- .../Test/Block/Adminhtml/LockedUsersGrid.php | 26 + .../Test/Block/Adminhtml/Role/PageActions.php | 2 +- .../Test/Block/Adminhtml/Role/RoleForm.php | 2 +- .../Test/Block/Adminhtml/Role/RoleForm.xml | 2 +- .../Test/Block/Adminhtml/Role/Tab/Role.php | 2 +- .../Block/Adminhtml/Role/Tab/User/Grid.php | 2 +- .../User/Test/Block/Adminhtml/RoleGrid.php | 2 +- .../Test/Block/Adminhtml/User/Edit/Form.php | 2 +- .../Block/Adminhtml/User/Edit/PageActions.php | 2 +- .../Block/Adminhtml/User/Edit/Tab/Roles.php | 2 +- .../Test/Block/Adminhtml/User/Tab/Role.php | 2 +- .../Block/Adminhtml/User/Tab/Role/Grid.php | 2 +- .../Test/Block/Adminhtml/User/UserForm.php | 2 +- .../Test/Block/Adminhtml/User/UserForm.xml | 2 +- .../User/Test/Block/Adminhtml/UserGrid.php | 2 +- .../AssertAccessTokensErrorRevokeMessage.php | 2 +- .../AssertImpossibleDeleteYourOwnAccount.php | 2 +- .../AssertImpossibleDeleteYourOwnRole.php | 2 +- .../AssertIncorrectUserPassword.php | 12 +- .../User/Test/Constraint/AssertRoleInGrid.php | 2 +- .../Test/Constraint/AssertRoleNotInGrid.php | 2 +- .../AssertRoleSuccessDeleteMessage.php | 2 +- .../AssertRoleSuccessSaveMessage.php | 2 +- .../Constraint/AssertUserDuplicateMessage.php | 2 +- ...sertUserFailedLoginByPermissionMessage.php | 2 +- .../AssertUserFailedLoginMessage.php | 2 +- .../User/Test/Constraint/AssertUserInGrid.php | 2 +- .../AssertUserInvalidEmailHostnameMessage.php | 2 +- .../AssertUserInvalidEmailMessage.php | 2 +- .../Test/Constraint/AssertUserNotInGrid.php | 2 +- .../AssertUserPasswordChangedSuccessfully.php | 47 + .../AssertUserRoleRestrictedAccess.php | 2 +- .../AssertUserSuccessDeleteMessage.php | 2 +- .../Constraint/AssertUserSuccessLogOut.php | 2 +- .../Constraint/AssertUserSuccessLogin.php | 2 +- .../AssertUserSuccessSaveMessage.php | 2 +- .../app/Magento/User/Test/Fixture/Role.xml | 2 +- .../User/Test/Fixture/Role/InRoleUsers.php | 2 +- .../app/Magento/User/Test/Fixture/User.xml | 2 +- .../Test/Fixture/User/CurrentPassword.php | 2 +- .../Magento/User/Test/Fixture/User/RoleId.php | 2 +- .../Magento/User/Test/Handler/Role/Curl.php | 2 +- .../User/Test/Handler/Role/RoleInterface.php | 2 +- .../Magento/User/Test/Handler/User/Curl.php | 2 +- .../User/Test/Handler/User/UserInterface.php | 2 +- .../User/Test/Page/Adminhtml/UserEdit.xml | 2 +- .../User/Test/Page/Adminhtml/UserIndex.xml | 2 +- .../User/Test/Page/Adminhtml/UserLocks.xml | 12 + .../Test/Page/Adminhtml/UserRoleEditRole.xml | 2 +- .../Test/Page/Adminhtml/UserRoleIndex.xml | 2 +- .../User/Test/Repository/ConfigData.xml | 20 +- .../app/Magento/User/Test/Repository/Role.xml | 32 +- .../app/Magento/User/Test/Repository/User.xml | 34 +- .../TestCase/CreateAdminUserEntityTest.php | 7 +- .../TestCase/CreateAdminUserEntityTest.xml | 16 +- .../CreateAdminUserRoleEntityTest.php | 2 +- .../CreateAdminUserRoleEntityTest.xml | 2 +- .../Test/TestCase/CustomAclPermissionTest.php | 62 + .../TestCase/DeleteAdminUserEntityTest.php | 7 +- .../TestCase/DeleteAdminUserEntityTest.xml | 4 +- .../TestCase/DeleteUserRoleEntityTest.php | 2 +- .../TestCase/DeleteUserRoleEntityTest.xml | 2 +- .../Test/TestCase/LockAdminUserEntityTest.php | 2 +- .../Test/TestCase/LockAdminUserEntityTest.xml | 2 +- .../User/Test/TestCase/NavigateMenuTest.xml | 2 +- ...lAccessTokensForAdminWithoutTokensTest.php | 2 +- ...lAccessTokensForAdminWithoutTokensTest.xml | 2 +- .../Test/TestCase/UnlockAdminUserTest.php | 144 + .../Test/TestCase/UnlockAdminUserTest.xml | 18 + .../TestCase/UpdateAdminUserEntityTest.php | 2 +- .../TestCase/UpdateAdminUserEntityTest.xml | 2 +- .../UpdateAdminUserRoleEntityTest.php | 2 +- .../UpdateAdminUserRoleEntityTest.xml | 2 +- ...ePasswordUserEntityPciRequirementsTest.php | 131 + ...ePasswordUserEntityPciRequirementsTest.xml | 19 + .../UserLoginAfterChangingPermissionsTest.php | 2 +- .../UserLoginAfterChangingPermissionsTest.xml | 2 +- .../User/Test/TestStep/CreateUserStep.php | 42 + .../Test/TestStep/LoginUserOnBackendStep.php | 2 +- .../Test/TestStep/LogoutUserOnBackendStep.php | 2 +- .../app/Magento/User/Test/etc/curl/di.xml | 4 +- .../Usps/Test/Repository/ConfigData.xml | 19 +- .../Test/TestCase/OnePageCheckoutTest.xml | 18 +- .../System/Variable/Edit/VariableForm.php | 2 +- .../System/Variable/Edit/VariableForm.xml | 2 +- .../System/Variable/FormPageActions.php | 2 +- .../Block/Adminhtml/System/Variable/Grid.php | 2 +- .../Constraint/AssertCustomVariableForm.php | 2 +- .../Constraint/AssertCustomVariableInGrid.php | 2 +- .../Constraint/AssertCustomVariableInPage.php | 2 +- .../AssertCustomVariableNotInCmsPageForm.php | 2 +- .../AssertCustomVariableNotInGrid.php | 2 +- .../AssertCustomVariableRestrictedAccess.php | 47 + ...sertCustomVariableSuccessDeleteMessage.php | 2 +- ...AssertCustomVariableSuccessSaveMessage.php | 2 +- .../Variable/Test/Fixture/SystemVariable.xml | 2 +- .../Test/Handler/SystemVariable/Curl.php | 2 +- .../SystemVariableInterface.php | 2 +- .../Page/Adminhtml/SystemVariableIndex.xml | 2 +- .../Test/Page/Adminhtml/SystemVariableNew.xml | 2 +- .../Test/Repository/SystemVariable.xml | 2 +- .../CreateCustomVariableEntityTest.php | 2 +- .../CreateCustomVariableEntityTest.xml | 3 +- .../Test/TestCase/CustomAclPermissionTest.xml | 16 + .../DeleteCustomVariableEntityTest.php | 2 +- .../DeleteCustomVariableEntityTest.xml | 3 +- .../Test/TestCase/NavigateMenuTest.xml | 2 +- .../UpdateCustomVariableEntityTest.php | 2 +- .../UpdateCustomVariableEntityTest.xml | 2 +- .../app/Magento/Variable/Test/etc/curl/di.xml | 2 +- .../Block/Onepage/Payment/Method/Vault.php | 67 + .../Vault/Test/Block/StoredPayments.php | 2 +- .../Magento/Vault/Test/Block/VaultPayment.php | 31 - .../AssertCreditCardNotPresentOnCheckout.php | 2 +- .../AssertSaveCreditCardOptionNotPresent.php | 40 + .../AssertStoredPaymentDeletedMessage.php | 2 +- .../Vault/Test/Page/CheckoutOnepage.xml | 4 +- .../Vault/Test/Page/StoredPaymentMethods.xml | 2 +- .../TestCase/CreateVaultOrderBackendTest.php | 3 +- .../TestCase/DeleteSavedCreditCardTest.php | 60 +- .../TestCase/DeleteSavedCreditCardTest.xml | 34 +- .../Test/TestCase/ReorderUsingVaultTest.php | 4 +- .../Test/TestCase/UseVaultOnCheckoutTest.php | 3 +- .../CheckSaveCreditCardOptionStep.php | 77 + .../DeleteCreditCardFromMyAccountStep.php | 41 +- .../Test/TestStep/DeleteStoredPaymentStep.php | 2 +- .../TestStep/SaveCreditCardOnBackendStep.php | 15 +- .../Test/TestStep/SaveCreditCardStep.php | 4 +- .../TestStep/UseSavedPaymentMethodStep.php | 15 +- .../TestStep/UseVaultPaymentTokenStep.php | 17 +- .../tests/app/Magento/Vault/Test/etc/di.xml | 19 + .../app/Magento/Vault/Test/etc/testcase.xml | 11 +- .../Edit/Section/ProductDetails/Fpt.php | 2 +- .../app/Magento/Weee/Test/Block/Cart.php | 2 +- .../Magento/Weee/Test/Block/Cart/CartItem.php | 2 +- .../Weee/Test/Block/Cart/CartItem/Fpt.php | 2 +- .../Magento/Weee/Test/Block/Cart/Totals.php | 2 +- .../Weee/Test/Block/Cart/Totals/Fpt.php | 2 +- .../Weee/Test/Block/Product/ListProduct.php | 2 +- .../Magento/Weee/Test/Block/Product/Price.php | 2 +- .../Block/Product/ProductList/ProductItem.php | 2 +- .../Magento/Weee/Test/Block/Product/View.php | 2 +- .../Weee/Test/Constraint/AssertFptApplied.php | 2 +- .../Page/Category/CatalogCategoryView.xml | 2 +- .../Magento/Weee/Test/Page/CheckoutCart.xml | 2 +- .../Test/Page/Product/CatalogProductView.xml | 4 +- .../Weee/Test/Repository/ConfigData.xml | 2 +- .../Test/TestCase/CreateTaxWithFptTest.php | 2 +- .../Test/TestCase/CreateTaxWithFptTest.xml | 8 +- .../tests/app/Magento/Weee/Test/etc/di.xml | 2 +- .../Block/Adminhtml/Widget/ChosenOption.php | 2 +- .../Widget/Instance/Edit/Tab/Parameters.php | 2 +- .../ParametersType/CatalogCategoryLink.php | 2 +- .../ParametersType/CatalogCategoryLink.xml | 2 +- .../CatalogCategoryLink/Form.php | 2 +- .../CatalogCategoryLink/Form.xml | 2 +- .../ParametersType/CatalogNewProductsList.php | 2 +- .../ParametersType/CatalogNewProductsList.xml | 2 +- .../Tab/ParametersType/CatalogProductLink.php | 2 +- .../Tab/ParametersType/CatalogProductLink.xml | 2 +- .../CatalogProductLink/Grid.php | 2 +- .../Edit/Tab/ParametersType/CmsPageLink.php | 2 +- .../Edit/Tab/ParametersType/CmsPageLink.xml | 2 +- .../Tab/ParametersType/CmsPageLink/Grid.php | 2 +- .../Tab/ParametersType/CmsStaticBlock.php | 2 +- .../Tab/ParametersType/CmsStaticBlock.xml | 2 +- .../ParametersType/CmsStaticBlock/Grid.php | 2 +- .../Tab/ParametersType/ParametersForm.php | 2 +- .../RecentlyComparedProducts.php | 2 +- .../RecentlyComparedProducts.xml | 2 +- .../ParametersType/RecentlyViewedProducts.php | 2 +- .../ParametersType/RecentlyViewedProducts.xml | 2 +- .../Widget/Instance/Edit/Tab/Settings.php | 2 +- .../Instance/Edit/Tab/WidgetInstance.php | 2 +- .../Tab/WidgetInstanceType/Categories.php | 2 +- .../Tab/WidgetInstanceType/Categories.xml | 2 +- .../Tab/WidgetInstanceType/GenericPages.php | 2 +- .../Tab/WidgetInstanceType/GenericPages.xml | 2 +- .../Tab/WidgetInstanceType/Product/Grid.php | 2 +- .../Edit/Tab/WidgetInstanceType/Products.php | 2 +- .../Edit/Tab/WidgetInstanceType/Products.xml | 2 +- .../WidgetInstanceType/WidgetInstanceForm.php | 2 +- .../Widget/Instance/Edit/WidgetForm.php | 2 +- .../Widget/Instance/Edit/WidgetForm.xml | 2 +- .../Block/Adminhtml/Widget/WidgetGrid.php | 33 +- .../Test/Block/Adminhtml/WidgetForm.php | 2 +- .../Test/Block/Adminhtml/WidgetForm.xml | 2 +- .../Magento/Widget/Test/Block/WidgetView.php | 2 +- .../AssertThemeFilterValuesOnWidgetGrid.php | 48 + .../AssertWidgetAbsentOnFrontendHome.php | 2 +- .../AssertWidgetCatalogCategoryLink.php | 2 +- .../AssertWidgetCatalogNewProductsList.php | 2 +- .../Constraint/AssertWidgetCmsPageLink.php | 2 +- .../Test/Constraint/AssertWidgetInGrid.php | 13 +- .../AssertWidgetOnFrontendInCatalog.php | 2 +- .../Constraint/AssertWidgetOnProductPage.php | 2 +- .../Constraint/AssertWidgetProductLink.php | 2 +- .../AssertWidgetRecentlyComparedProducts.php | 3 +- .../AssertWidgetRecentlyViewedProducts.php | 2 +- .../AssertWidgetSuccessDeleteMessage.php | 2 +- .../AssertWidgetSuccessSaveMessage.php | 2 +- .../Test/Constraint/AssertWidgetsInGrid.php | 48 + .../Magento/Widget/Test/Fixture/Widget.xml | 2 +- .../Widget/Test/Fixture/Widget/Parameters.php | 2 +- .../Widget/Test/Fixture/Widget/StoreIds.php | 2 +- .../Test/Fixture/Widget/WidgetInstance.php | 2 +- .../Widget/Test/Handler/Widget/Curl.php | 3 +- .../Test/Handler/Widget/WidgetInterface.php | 2 +- .../Page/Adminhtml/WidgetInstanceEdit.xml | 2 +- .../Page/Adminhtml/WidgetInstanceIndex.xml | 2 +- .../Test/Page/Adminhtml/WidgetInstanceNew.xml | 2 +- .../Magento/Widget/Test/Repository/Widget.xml | 17 +- .../Test/Repository/Widget/Parameters.xml | 2 +- .../Test/Repository/Widget/WidgetInstance.xml | 2 +- .../AbstractCreateWidgetEntityTest.php | 2 +- .../Test/TestCase/CreateWidgetEntityTest.php | 4 +- .../Test/TestCase/CreateWidgetEntityTest.xml | 10 +- .../Test/TestCase/CreateWidgetsEntityTest.php | 57 + .../Test/TestCase/CreateWidgetsEntityTest.xml | 18 + .../Test/TestCase/DeleteWidgetEntityTest.php | 2 +- .../Test/TestCase/DeleteWidgetEntityTest.xml | 2 +- .../Widget/Test/TestCase/NavigateMenuTest.xml | 2 +- .../Test/TestStep/DeleteAllWidgetsStep.php | 2 +- .../app/Magento/Widget/Test/etc/curl/di.xml | 2 +- .../tests/app/Magento/Widget/Test/etc/di.xml | 12 +- .../Adminhtml/Customer/Edit/Tab/Wishlist.php | 2 +- .../Customer/Edit/Tab/Wishlist/Grid.php | 2 +- .../Block/Adminhtml/Edit/CustomerForm.xml | 2 +- .../Wishlist/Test/Block/Customer/Sharing.php | 2 +- .../Wishlist/Test/Block/Customer/Sharing.xml | 2 +- .../Wishlist/Test/Block/Customer/Wishlist.php | 3 +- .../Test/Block/Customer/Wishlist/Items.php | 2 +- .../Block/Customer/Wishlist/Items/Product.php | 29 +- .../Block/Customer/Wishlist/Items/Product.xml | 2 +- .../AbstractAssertWishlistProductDetails.php | 2 +- ...sertAddProductToWishlistSuccessMessage.php | 2 +- ...AssertCustomerWishlistOnBackendIsEmpty.php | 42 + ...ertMoveProductToWishlistSuccessMessage.php | 2 +- .../AssertProductDetailsInWishlist.php | 2 +- ...ProductInCustomerWishlistOnBackendGrid.php | 2 +- ...ductIsPresentInCustomerBackendWishlist.php | 2 +- .../AssertProductIsPresentInWishlist.php | 2 +- .../AssertProductPriceIsNotZero.php | 47 + .../AssertProductsIsAbsentInWishlist.php | 2 +- ...uctsIsPresentInCustomerBackendWishlist.php | 54 + .../Test/Constraint/AssertWishlistIsEmpty.php | 2 +- .../Constraint/AssertWishlistShareMessage.php | 2 +- .../Wishlist/Test/Page/WishlistIndex.xml | 2 +- .../Wishlist/Test/Page/WishlistShare.xml | 2 +- .../Test/TestCase/AbstractWishlistTest.php | 2 +- .../AddProductToWishlistEntityTest.php | 7 +- .../AddProductToWishlistEntityTest.xml | 12 +- ...CartFromCustomerWishlistOnFrontendTest.php | 3 +- ...CartFromCustomerWishlistOnFrontendTest.xml | 7 +- ...ProductInCustomerWishlistOnBackendTest.php | 2 +- ...ProductInCustomerWishlistOnBackendTest.xml | 3 +- ...roductInCustomerWishlistOnFrontendTest.php | 3 +- ...roductInCustomerWishlistOnFrontendTest.xml | 7 +- ...oductFromCustomerWishlistOnBackendTest.php | 2 +- ...oductFromCustomerWishlistOnBackendTest.xml | 2 +- ...leteProductsFromWishlistOnFrontendTest.php | 2 +- ...leteProductsFromWishlistOnFrontendTest.xml | 7 +- ...eProductFromShoppingCartToWishlistTest.php | 2 +- ...eProductFromShoppingCartToWishlistTest.xml | 6 +- .../Test/TestCase/ShareWishlistEntityTest.php | 2 +- .../Test/TestCase/ShareWishlistEntityTest.xml | 2 +- ...ProductInCustomerWishlistOnBackendTest.php | 2 +- ...ProductInCustomerWishlistOnBackendTest.xml | 3 +- .../TestStep/AddProductsToWishlistStep.php | 2 +- .../Magento/Mtf/TestSuite/InjectableTests.php | 2 +- .../TestSuite/InjectableTests/3rd_party.xml | 2 +- .../TestSuite/InjectableTests/acceptance.xml | 2 +- .../InjectableTests/acceptance_unstable.xml | 2 +- .../Mtf/TestSuite/InjectableTests/basic.xml | 7 +- .../TestSuite/InjectableTests/basic_green.xml | 29 + .../TestSuite/InjectableTests/category.xml | 2 +- .../InjectableTests/extended_acceptance.xml | 2 +- .../InjectableTests/installation.xml | 2 +- .../Mtf/TestSuite/InjectableTests/mvp.xml | 2 +- .../Mtf/TestSuite/InjectableTests/setup.xml | 2 +- .../Mtf/TestSuite/InjectableTests/upgrade.xml | 4 +- dev/tests/functional/utils/bootstrap.php | 2 +- dev/tests/functional/utils/command.php | 4 +- .../utils/deleteMagentoGeneratedCode.php | 7 + dev/tests/functional/utils/export.php | 27 + dev/tests/functional/utils/generate.php | 9 +- .../functional/utils/generate/factory.php | 2 +- .../functional/utils/generate/fixture.php | 2 +- .../functional/utils/generate/handler.php | 2 +- dev/tests/functional/utils/generate/page.php | 2 +- .../functional/utils/generate/repository.php | 2 +- .../functional/utils/generateFixtureXml.php | 2 +- dev/tests/functional/utils/log.php | 14 + dev/tests/functional/utils/mtf | 11 + dev/tests/functional/utils/pathChecker.php | 17 + dev/tests/functional/utils/website.php | 2 +- .../etc/module.xml | 14 + .../etc/zip_codes.xml | 20 + .../registration.php | 12 + .../Magento/TestModuleSample/composer.json | 2 +- .../Magento/TestModuleSample/etc/module.xml | 2 +- .../Magento/TestModuleSample/registration.php | 2 +- .../integration/etc/config-global.php.dist | 2 +- .../integration/etc/di/preferences/ce.php | 7 +- .../etc/install-config-mysql.php.dist | 2 +- .../etc/install-config-mysql.travis.php.dist | 2 +- .../Annotation/AdminConfigFixture.php | 11 +- .../TestFramework/Annotation/AppArea.php | 2 +- .../TestFramework/Annotation/AppIsolation.php | 2 +- .../TestFramework/Annotation/Cache.php | 2 +- .../Annotation/ComponentRegistrarFixture.php | 2 +- .../Annotation/ConfigFixture.php | 7 +- .../TestFramework/Annotation/DataFixture.php | 2 +- .../DataFixtureBeforeTransaction.php | 2 +- .../TestFramework/Annotation/DbIsolation.php | 2 +- .../Api/Config/Reader/FileResolver.php | 2 +- .../Magento/TestFramework/App/Config.php | 137 + .../TestFramework/App/EnvironmentFactory.php | 2 +- .../Magento/TestFramework/App/Filesystem.php | 2 +- .../TestFramework/App/MutableScopeConfig.php | 80 + .../ObjectManager/Environment/Developer.php | 2 +- .../TestFramework/App/ReinitableConfig.php | 43 + .../Magento/TestFramework/App/State.php | 2 +- .../Magento/TestFramework/Application.php | 8 +- .../TestFramework/Backend/App/Config.php | 46 + .../Magento/TestFramework/Bootstrap.php | 2 +- .../TestFramework/Bootstrap/DocBlock.php | 7 +- .../TestFramework/Bootstrap/Environment.php | 2 +- .../TestFramework/Bootstrap/Memory.php | 2 +- .../TestFramework/Bootstrap/MemoryFactory.php | 2 +- .../TestFramework/Bootstrap/Profiler.php | 2 +- .../TestFramework/Bootstrap/Settings.php | 2 +- .../Magento/TestFramework/Config.php | 2 +- .../Magento/TestFramework/CookieManager.php | 2 +- .../Magento/TestFramework/Db/AbstractDb.php | 2 +- .../TestFramework/Db/Adapter/Mysql.php | 2 +- .../Db/Adapter/TransactionInterface.php | 2 +- .../TestFramework/Db/ConnectionAdapter.php | 2 +- .../Magento/TestFramework/Db/Mysql.php | 2 +- .../Magento/TestFramework/Db/Sequence.php | 2 +- .../TestFramework/Db/Sequence/Builder.php | 2 +- .../Magento/TestFramework/Entity.php | 2 +- .../TestFramework/ErrorLog/Listener.php | 2 +- .../Magento/TestFramework/ErrorLog/Logger.php | 2 +- .../Magento/TestFramework/Event/Magento.php | 2 +- .../TestFramework/Event/Param/Transaction.php | 2 +- .../Magento/TestFramework/Event/PhpUnit.php | 2 +- .../TestFramework/Event/Transaction.php | 2 +- .../Magento/TestFramework/EventManager.php | 2 +- .../Magento/TestFramework/Helper/Api.php | 2 +- .../TestFramework/Helper/Bootstrap.php | 2 +- .../TestFramework/Helper/CacheCleaner.php | 50 + .../Magento/TestFramework/Helper/Config.php | 2 +- .../Magento/TestFramework/Helper/Eav.php | 2 +- .../Magento/TestFramework/Helper/Factory.php | 2 +- .../Magento/TestFramework/Helper/Memory.php | 2 +- .../TestFramework/Indexer/TestCase.php | 2 +- .../TestFramework/Interception/PluginList.php | 19 +- .../TestFramework/Isolation/AppConfig.php | 50 + .../Isolation/DeploymentConfig.php | 2 +- .../Isolation/WorkingDirectory.php | 2 +- .../TestFramework/Listener/ExtededTestdox.php | 2 +- .../Mail/Template/TransportBuilderMock.php | 2 +- .../Mail/TransportInterfaceMock.php | 2 +- .../Magento/TestFramework/MemoryLimit.php | 2 +- .../Magento/TestFramework/ObjectManager.php | 3 +- .../TestFramework/ObjectManager/Config.php | 2 +- .../ObjectManager/Configurator.php | 26 +- .../TestFramework/ObjectManagerFactory.php | 2 +- .../TestFramework/Profiler/OutputBamboo.php | 2 +- .../Magento/TestFramework/Request.php | 2 +- .../Magento/TestFramework/Response.php | 2 +- .../TestFramework/Store/StoreManager.php | 19 +- .../TestCase/AbstractBackendController.php | 2 +- .../TestCase/AbstractConfigFiles.php | 2 +- .../TestCase/AbstractController.php | 2 +- .../TestCase/AbstractIntegrity.php | 2 +- .../Magento/TestFramework/View/Layout.php | 2 +- .../Workaround/Cleanup/StaticProperties.php | 2 +- .../Workaround/Cleanup/TestCaseProperties.php | 2 +- .../TestFramework/Workaround/Segfault.php | 2 +- dev/tests/integration/framework/autoload.php | 2 +- dev/tests/integration/framework/bootstrap.php | 2 +- .../framework/deployTestModules.php | 2 +- .../tests/unit/framework/bootstrap.php | 2 +- .../framework/tests/unit/phpunit.xml.dist | 2 +- .../Annotation/AdminConfigFixtureTest.php | 2 +- .../Magento/Test/Annotation/AppAreaTest.php | 2 +- .../Test/Annotation/AppIsolationTest.php | 2 +- .../ComponentRegistrarFixtureTest.php | 2 +- .../Test/Annotation/ConfigFixtureTest.php | 2 +- .../Test/Annotation/DataFixtureTest.php | 2 +- .../Test/Annotation/DbIsolationTest.php | 2 +- .../components/a/aa/aaa/registration.php | 2 +- .../_files/components/a/aa/registration.php | 2 +- .../_files/components/b/registration.php | 2 +- .../_files/components/registration.php | 2 +- .../_files/sample_fixture_two_rollback.php | 2 +- .../testsuite/Magento/Test/App/ConfigTest.php | 50 + .../Magento/Test/ApplicationTest.php | 2 +- .../Magento/Test/Bootstrap/DocBlockTest.php | 2 +- .../Test/Bootstrap/EnvironmentTest.php | 2 +- .../Magento/Test/Bootstrap/MemoryTest.php | 2 +- .../Magento/Test/Bootstrap/ProfilerTest.php | 2 +- .../Magento/Test/Bootstrap/SettingsTest.php | 2 +- .../Magento/Test/Bootstrap/_files/1.xml | 2 +- .../Magento/Test/Bootstrap/_files/1.xml.dist | 2 +- .../Magento/Test/Bootstrap/_files/2.xml | 2 +- .../Magento/Test/Bootstrap/_files/3.xml.dist | 2 +- .../Magento/Test/Bootstrap/_files/4.php | 2 +- .../Magento/Test/Bootstrap/_files/metrics.php | 2 +- .../testsuite/Magento/Test/BootstrapTest.php | 2 +- .../Db/Adapter/TransactionInterfaceTest.php | 2 +- .../testsuite/Magento/Test/EntityTest.php | 2 +- .../Magento/Test/Event/MagentoTest.php | 2 +- .../Test/Event/Param/TransactionTest.php | 2 +- .../Magento/Test/Event/PhpUnitTest.php | 2 +- .../Magento/Test/Event/TransactionTest.php | 2 +- .../Magento/Test/EventManagerTest.php | 2 +- .../Magento/Test/Helper/BootstrapTest.php | 2 +- .../Magento/Test/Helper/FactoryTest.php | 2 +- .../Magento/Test/Helper/MemoryTest.php | 2 +- .../Magento/Test/Isolation/AppConfigTest.php | 48 + .../Test/Isolation/WorkingDirectoryTest.php | 2 +- .../Magento/Test/MemoryLimitTest.php | 2 +- .../Magento/Test/ObjectManagerTest.php | 2 +- .../Test/Profiler/OutputBambooTest.php | 2 +- .../Test/Profiler/OutputBambooTestFilter.php | 2 +- .../testsuite/Magento/Test/RequestTest.php | 2 +- .../Test/TestCase/ControllerAbstractTest.php | 2 +- .../Cleanup/TestCasePropertiesTest.php | 2 +- .../TestCasePropertiesTest/DummyTestCase.php | 2 +- dev/tests/integration/phpunit.xml.dist | 2 +- .../Adminhtml/Notification/MarkAsReadTest.php | 2 +- .../Notification/MassMarkAsReadTest.php | 2 +- .../Adminhtml/Notification/MassRemoveTest.php | 2 +- .../Adminhtml/Notification/RemoveTest.php | 2 +- .../Inbox/Collection/CriticalTest.php | 2 +- .../_files/notifications.php | 2 +- .../Model/Export/AdvancedPricingTest.php | 24 +- .../Model/Import/AdvancedPricingTest.php | 64 +- .../Import/_files/import_advanced_pricing.csv | 16 +- .../_files/create_products.php | 2 +- .../_files/product_with_second_website.php | 2 +- .../Magento/Analytics/_files/create_link.php | 21 + .../ResourceModel/Role/CollectionTest.php | 2 +- .../Role/Grid/CollectionTest.php | 2 +- .../Model/ResourceModel/RoleTest.php | 2 +- .../ResourceModel/Rules/CollectionTest.php | 2 +- .../Magento/Authorization/Model/RoleTest.php | 2 +- .../Magento/Authorization/Model/RulesTest.php | 21 +- .../Directpost/Payment/PlaceTest.php | 2 +- .../Directpost/Payment/PlaceTesting.php | 2 +- .../Directpost/Payment/ResponseTest.php | 245 + .../Controller/Directpost/PaymentTest.php | 2 +- .../Model/Directpost/RequestTest.php | 102 + .../Authorizenet/Model/DirectpostTest.php | 129 + .../Magento/Authorizenet/_files/order.php | 70 + .../Backend/App/AbstractActionTest.php | 2 +- .../Magento/Backend/App/RouterTest.php | 2 +- .../Backend/Block/Dashboard/GraphTest.php | 2 +- .../Dashboard/Tab/Products/ViewedTest.php | 65 + .../Magento/Backend/Block/MenuTest.php | 21 +- .../Magento/Backend/Block/Page/FooterTest.php | 2 +- .../Magento/Backend/Block/Page/HeaderTest.php | 2 +- .../Block/System/Account/Edit/FormTest.php | 2 +- .../System/Design/Edit/Tab/GeneralTest.php | 2 +- .../Backend/Block/System/Store/DeleteTest.php | 2 +- .../System/Store/Edit/Form/GroupTest.php | 2 +- .../System/Store/Edit/Form/StoreTest.php | 2 +- .../System/Store/Edit/Form/WebsiteTest.php | 2 +- .../Backend/Block/System/Store/EditTest.php | 2 +- .../Magento/Backend/Block/TemplateTest.php | 24 +- .../Backend/Block/Widget/ContainerTest.php | 2 +- .../Block/Widget/Form/ContainerTest.php | 2 +- .../Magento/Backend/Block/Widget/FormTest.php | 2 +- .../Widget/Grid/Column/Renderer/TextTest.php | 142 + .../Block/Widget/Grid/ColumnSetTest.php | 2 +- .../Block/Widget/Grid/ContainerTest.php | 2 +- .../Block/Widget/Grid/ExtendedTest.php | 2 +- .../Backend/Block/Widget/Grid/ItemTest.php | 2 +- .../Widget/Grid/Massaction/AdditionalTest.php | 2 +- .../Block/Widget/Grid/MassactionTest.php | 123 +- .../Magento/Backend/Block/Widget/GridTest.php | 2 +- .../Magento/Backend/Block/Widget/TabsTest.php | 2 +- .../Magento/Backend/Block/WidgetTest.php | 2 +- .../layout/layout_test_grid_handle.xml | 86 +- .../Magento/test_default/registration.php | 2 +- .../adminhtml/Magento/test_default/theme.xml | 2 +- .../Block/_files/form_key_disabled.php | 2 +- .../_files/form_key_disabled_rollback.php | 2 +- .../Magento/Backend/etc/adminhtml/menu.xml | 2 +- .../Backend/Controller/Adminhtml/AuthTest.php | 2 +- .../Adminhtml/Cache/CleanStaticFilesTest.php | 2 +- .../Adminhtml/Cache/MassActionTest.php | 56 +- .../Controller/Adminhtml/CacheTest.php | 2 +- .../Dashboard/ProductsViewedTest.php | 2 +- .../Controller/Adminhtml/DashboardTest.php | 2 +- .../Controller/Adminhtml/IndexTest.php | 2 +- .../Adminhtml/System/AccountTest.php | 2 +- .../Adminhtml/System/DesignTest.php | 2 +- .../Controller/Adminhtml/System/StoreTest.php | 2 +- .../Controller/Adminhtml/UrlRewriteTest.php | 2 +- .../Magento/Backend/Helper/DataTest.php | 2 +- .../Backend/Model/Auth/SessionTest.php | 2 +- .../Magento/Backend/Model/AuthTest.php | 2 +- .../Backend/Model/Locale/ResolverTest.php | 2 +- .../Magento/Backend/Model/MenuTest.php | 124 +- .../Backend/Model/Search/CustomerTest.php | 2 +- .../Backend/Model/Search/OrderTest.php | 2 +- .../Backend/Model/Session/AdminConfigTest.php | 2 +- .../Backend/Model/Session/QuoteTest.php | 2 +- .../Magento/Backend/Model/SessionTest.php | 2 +- .../Backend/Model/Translate/InlineTest.php | 2 +- .../Magento/Backend/Model/UrlTest.php | 2 +- .../_files/cache/all_types_invalidated.php | 2 +- .../cache/all_types_invalidated_rollback.php | 2 +- .../_files/cache/application_cache.php | 2 +- .../cache/application_cache_rollback.php | 2 +- .../_files/cache/empty_storage.php | 2 +- .../_files/cache/non_application_cache.php | 2 +- .../cache/non_application_cache_rollback.php | 2 +- .../Braintree/Block/Form/ContainerTest.php | 2 +- .../Block/VaultTokenRendererTest.php | 2 +- .../Adminhtml/Order/PaymentReviewTest.php | 2 +- .../Controller/Cards/DeleteActionTest.php | 2 +- .../Braintree/Gateway/Config/ConfigTest.php | 70 + .../System/Config/CountryCreditCardTest.php | 69 + .../Braintree/Model/PaymentMethodListTest.php | 71 + .../PayPal/TokenUiComponentProviderTest.php | 62 + .../Model/Ui/TokensConfigProviderTest.php | 4 +- .../Magento/Braintree/_files/fraud_order.php | 2 +- .../Magento/Braintree/_files/payments.php | 16 + .../Magento/Braintree/_files/paypal_quote.php | 2 +- .../Braintree/_files/paypal_vault_token.php | 4 +- .../Tab/Bundle/Option/Search/GridTest.php | 2 +- .../Edit/Tab/Bundle/Option/SearchTest.php | 2 +- .../Magento/Bundle/Controller/ProductTest.php | 2 +- .../Model/Product/BundlePriceAbstract.php | 130 + .../DynamicBundlePriceCalculatorTest.php | 327 + ...ndleWithCatalogPriceRuleCalculatorTest.php | 433 + ...icBundleWithSpecialPriceCalculatorTest.php | 339 + ...namicBundleWithTierPriceCalculatorTest.php | 644 ++ .../FixedBundlePriceCalculatorTest.php | 394 + ...ndleWithCatalogPriceRuleCalculatorTest.php | 810 ++ ...edBundleWithSpecialPriceCalculatorTest.php | 822 ++ ...FixedBundleWithTierPriceCalculatorTest.php | 915 ++ .../Bundle/Model/Product/IsSaleableTest.php | 392 + .../Bundle/Model/Product/OptionListTest.php | 2 +- .../Bundle/Model/Product/PriceTest.php | 2 +- .../Magento/Bundle/Model/Product/TypeTest.php | 2 +- .../Magento/Bundle/Model/ProductTest.php | 2 +- .../dynamic_bundle_product.php | 27 + .../dynamic_bundle_product_rollback.php | 25 + ...namic_bundle_product_with_catalog_rule.php | 8 + ...dle_product_with_catalog_rule_rollback.php | 7 + ...amic_bundle_product_with_special_price.php | 27 + ...le_product_with_special_price_rollback.php | 7 + .../PriceCalculator/fixed_bundle_product.php | 28 + .../fixed_bundle_product_rollback.php | 25 + ...fixed_bundle_product_with_catalog_rule.php | 8 + ...dle_product_with_catalog_rule_rollback.php | 7 + ...ixed_bundle_product_with_special_price.php | 17 + ...le_product_with_special_price_rollback.php | 7 + .../Bundle/_files/issaleable_product.php | 210 + .../_files/issaleable_product_rollback.php | 28 + .../Bundle/_files/multiple_products.php | 129 + .../_files/multiple_products_rollback.php | 28 + .../order_item_with_bundle_and_options.php | 2 +- ..._item_with_bundle_and_options_rollback.php | 2 +- .../Magento/Bundle/_files/product.php | 2 +- .../Bundle/_files/product_rollback.php | 2 +- .../_files/product_with_multiple_options.php | 2 +- ...product_with_multiple_options_rollback.php | 2 +- .../_files/product_with_tier_pricing.php | 5 +- .../BundleImportExport/Model/BundleTest.php | 2 +- .../Model/Export/RowCustomizerTest.php | 8 +- .../Model/Import/Product/Type/BundleTest.php | 2 +- .../Adminhtml/Captcha/DefaultCaptchaTest.php | 2 +- .../Captcha/Block/Captcha/DefaultTest.php | 2 +- ...tionWithInvalidCaptchaReturnsErrorTest.php | 2 +- ...IsRequiredAfterFailedLoginAttemptsTest.php | 2 +- ...successfulMessageWhenCaptchaFailedTest.php | 2 +- ...otPasswordBackendWhenCaptchaFailedTest.php | 2 +- .../Magento/Captcha/_files/dummy_user.php | 2 +- .../Category/Checkboxes/TreeTest.php | 2 +- .../Block/Adminhtml/Category/TreeTest.php | 2 +- .../Product/Attribute/Edit/Tab/FrontTest.php | 2 +- .../Product/Attribute/Set/Toolbar/AddTest.php | 2 +- .../Block/Adminhtml/Product/Edit/JsTest.php | 2 +- .../Product/Edit/Tab/Options/OptionTest.php | 2 +- .../Edit/Tab/Options/Type/SelectTest.php | 2 +- .../Product/Helper/Form/CategoryTest.php | 2 +- .../Helper/Form/Gallery/ContentTest.php | 2 +- .../Product/Helper/Form/WeightTest.php | 2 +- .../Adminhtml/Product/Options/AjaxTest.php | 2 +- .../Catalog/Block/Product/AbstractTest.php | 2 +- .../Catalog/Block/Product/ListTest.php | 2 +- .../Magento/Catalog/Block/Product/NewTest.php | 2 +- .../Product/ProductList/CrosssellTest.php | 2 +- .../Block/Product/ProductList/RelatedTest.php | 2 +- .../Block/Product/ProductList/ToolbarTest.php | 2 +- .../Block/Product/View/AdditionalTest.php | 2 +- .../Block/Product/View/OptionsTest.php | 2 +- .../Catalog/Block/Product/ViewTest.php | 2 +- .../Command/ProductAttributesCleanUpTest.php | 2 +- .../Controller/Adminhtml/CategoryTest.php | 6 +- .../Product/Action/AttributeTest.php | 56 +- .../Adminhtml/Product/AttributeTest.php | 2 +- .../Adminhtml/Product/ReviewTest.php | 2 +- .../Adminhtml/Product/Set/DeleteTest.php | 2 +- .../Adminhtml/Product/Set/SaveTest.php | 64 + .../Controller/Adminhtml/ProductTest.php | 2 +- .../Catalog/Controller/CategoryTest.php | 2 +- .../Magento/Catalog/Controller/IndexTest.php | 2 +- .../Controller/Product/CompareTest.php | 2 +- .../Catalog/Controller/Product/ViewTest.php | 2 +- .../Catalog/Controller/ProductTest.php | 2 +- .../Cron/DeleteOutdatedPriceValuesTest.php | 130 + .../Magento/Catalog/Helper/CategoryTest.php | 2 +- .../Magento/Catalog/Helper/DataTest.php | 5 +- .../Magento/Catalog/Helper/OutputTest.php | 2 +- .../Catalog/Helper/Product/CompareTest.php | 2 +- .../Catalog/Helper/Product/CompositeTest.php | 2 +- .../Catalog/Helper/Product/FlatTest.php | 7 +- .../Product/Stub/ProductControllerStub.php | 2 +- .../Catalog/Helper/Product/ViewTest.php | 2 +- .../Magento/Catalog/Helper/ProductTest.php | 2 +- .../Catalog/Model/AbstractModel/Stub.php | 2 +- .../Magento/Catalog/Model/AbstractTest.php | 2 +- .../Model/Category/CategoryImageTest.php | 99 - .../StubZendLogWriterStream.php | 34 - .../Model/Category/DataProviderTest.php | 2 +- .../_files/category_without_image.php | 2 +- .../_files/service_category_create.php | 2 +- .../service_category_create_rollback.php | 2 +- .../Magento/Catalog/Model/CategoryTest.php | 23 +- .../Catalog/Model/CategoryTreeTest.php | 2 +- .../Magento/Catalog/Model/ConfigTest.php | 61 + .../Magento/Catalog/Model/DesignTest.php | 2 +- .../Model/Indexer/Category/ProductTest.php | 5 +- .../Catalog/Model/Indexer/FlatTest.php | 2 +- .../Indexer/Product/Eav/Action/FullTest.php | 2 +- .../Indexer/Product/Eav/Action/RowTest.php | 2 +- .../Indexer/Product/Eav/Action/RowsTest.php | 2 +- .../Indexer/Product/Flat/Action/FullTest.php | 2 +- .../Indexer/Product/Flat/Action/RowTest.php | 2 +- .../Indexer/Product/Flat/Action/RowsTest.php | 2 +- .../Indexer/Product/Flat/ProcessorTest.php | 2 +- .../Indexer/Product/Price/Action/FullTest.php | 2 +- .../Indexer/Product/Price/Action/RowTest.php | 2 +- .../Indexer/Product/Price/Action/RowsTest.php | 2 +- .../Catalog/Model/Layer/CategoryTest.php | 2 +- .../Model/Layer/Filter/AttributeTest.php | 2 +- .../Model/Layer/Filter/CategoryTest.php | 2 +- .../Layer/Filter/DataProvider/PriceTest.php | 2 +- .../Model/Layer/Filter/DecimalTest.php | 2 +- .../Filter/Price/AlgorithmAdvancedTest.php | 2 +- .../Layer/Filter/Price/AlgorithmBaseTest.php | 2 +- .../Price/_files/_algorithm_base_data.php | 2 +- .../Filter/Price/_files/products_advanced.php | 2 +- .../_files/products_advanced_rollback.php | 2 +- .../Filter/Price/_files/products_base.php | 2 +- .../Price/_files/products_base_rollback.php | 2 +- .../Catalog/Model/Layer/Filter/PriceTest.php | 2 +- .../_files/attribute_weight_filterable.php | 2 +- .../Filter/_files/attribute_with_option.php | 2 +- .../_files/attribute_with_option_rollback.php | 2 +- .../Catalog/Model/Product/ActionTest.php | 2 +- .../Product/Attribute/Backend/PriceTest.php | 277 +- .../Product/Attribute/Backend/SkuTest.php | 2 +- .../Attribute/Backend/TierpriceTest.php | 2 +- .../Source/CountryofmanufactureTest.php | 32 + .../_files/create_attribute_service.php | 2 +- .../create_attribute_service_rollback.php | 2 +- .../Attribute/_files/select_attribute.php | 2 +- .../_files/select_attribute_rollback.php | 2 +- .../Model/Product/Compare/ListCompareTest.php | 2 +- .../Product/Gallery/CreateHandlerTest.php | 141 +- .../Model/Product/Gallery/ProcessorTest.php | 14 +- .../Model/Product/Gallery/ReadHandlerTest.php | 2 +- .../Catalog/Model/Product/ImageTest.php | 13 +- .../Model/Product/Option/Type/DateTest.php | 112 + .../Option/Type/File/ValidatorFileTest.php | 2 +- .../Option/Type/File/ValidatorInfoTest.php | 2 +- .../Model/Product/Type/AbstractTypeTest.php | 10 +- .../Catalog/Model/Product/Type/PriceTest.php | 2 +- .../Model/Product/Type/VirtualTest.php | 2 +- .../Catalog/Model/Product/TypeTest.php | 2 +- .../Magento/Catalog/Model/Product/UrlTest.php | 2 +- .../Product/_files/service_product_create.php | 2 +- .../service_product_create_rollback.php | 2 +- .../Catalog/Model/ProductExternalTest.php | 2 +- .../Catalog/Model/ProductGettersTest.php | 2 +- .../Catalog/Model/ProductPriceTest.php | 2 +- .../Magento/Catalog/Model/ProductTest.php | 33 +- .../Model/ResourceModel/Eav/AttributeTest.php | 2 +- .../ResourceModel/Product/CollectionTest.php | 2 +- .../Product/Indexer/Eav/SourceTest.php | 3 +- .../Product/Link/Product/CollectionTest.php | 2 +- .../ResourceModel/_files/product_simple.php | 2 +- .../ResourceModel/_files/url_rewrites.php | 2 +- .../Option/Type/File/ProcessorTest.php | 2 +- ...hPriceAttributeScopeOnConfigChangeTest.php | 67 + .../Pricing/Render/FinalPriceBoxTest.php | 147 + .../Product/Form/Modifier/CategoriesTest.php | 50 + .../Product/Form/Modifier/EavTest.php | 2 +- .../_files/eav_expected_data_output.php | 2 +- .../_files/eav_expected_meta_output.php | 2 +- .../eav_expected_meta_output_w_default.php | 2 +- .../Modifier/_files/expected_categories.php | 89 + .../_files/input_meta_for_categories.php | 56 + .../testsuite/Magento/Catalog/WidgetTest.php | 2 +- .../attribute_set_with_image_attribute.php | 2 +- ...bute_set_with_image_attribute_rollback.php | 2 +- .../attribute_set_with_renamed_group.php | 54 + .../Magento/Catalog/_files/categories.php | 2 +- .../Catalog/_files/categories_no_products.php | 2 +- .../categories_no_products_rollback.php | 2 +- .../Catalog/_files/categories_rollback.php | 2 +- .../Magento/Catalog/_files/category.php | 2 +- .../Catalog/_files/category_attribute.php | 2 +- .../_files/category_attribute_rollback.php | 2 +- .../Catalog/_files/category_backend.php | 2 +- .../_files/category_backend_rollback.php | 2 +- .../Catalog/_files/category_duplicates.php | 2 +- .../Catalog/_files/category_product.php | 2 +- .../_files/category_product_rollback.php | 2 +- .../Catalog/_files/category_rollback.php | 2 +- .../Magento/Catalog/_files/category_tree.php | 2 +- .../Catalog/_files/category_tree_rollback.php | 2 +- .../Catalog/_files/category_with_position.php | 2 +- .../category_with_position_rollback.php | 2 +- .../Catalog/_files/dropdown_attribute.php | 59 + .../Catalog/_files/empty_attribute_group.php | 2 +- .../_files/empty_attribute_group_rollback.php | 2 +- .../_files/enable_price_index_schedule.php | 10 + .../enable_price_index_schedule_rollback.php | 10 + .../_files/enable_reindex_schedule.php | 2 +- .../enable_reindex_schedule_rollback.php | 2 +- .../_files/etc/extension_attributes.xml | 2 +- .../Catalog/_files/filterable_attributes.php | 2 +- .../_files/indexer_catalog_category.php | 2 +- .../indexer_catalog_category_rollback.php | 2 +- .../Catalog/_files/multiple_products.php | 2 +- .../_files/multiple_products_rollback.php | 2 +- .../Catalog/_files/multiselect_attribute.php | 5 +- .../_files/multiselect_attribute_rollback.php | 18 + ...select_attribute_with_incorrect_values.php | 55 + ...r_item_with_product_and_custom_options.php | 2 +- ...th_product_and_custom_options_rollback.php | 2 +- .../Catalog/_files/price_row_fixture.php | 2 +- .../_files/price_row_fixture_rollback.php | 2 +- .../Catalog/_files/product_associated.php | 2 +- .../_files/product_associated_rollback.php | 2 +- .../Catalog/_files/product_attribute.php | 2 +- .../_files/product_attribute_rollback.php | 2 +- ...roduct_attribute_with_invalid_apply_to.php | 2 +- .../_files/product_different_store_prices.php | 58 + ...roduct_different_store_prices_rollback.php | 26 + .../_files/product_group_prices_rollback.php | 2 +- .../product_has_tier_price_show_as_low_as.php | 88 + ...has_tier_price_show_as_low_as_rollback.php | 24 + .../Magento/Catalog/_files/product_image.php | 2 +- .../Catalog/_files/product_image_rollback.php | 2 +- .../Magento/Catalog/_files/product_simple.php | 14 +- .../_files/product_simple_duplicated.php | 2 +- .../product_simple_duplicated_rollback.php | 2 +- .../_files/product_simple_multistore.php | 2 +- .../product_simple_multistore_rollback.php | 2 +- .../_files/product_simple_rollback.php | 2 +- .../product_simple_with_admin_store.php | 10 +- .../product_simple_with_custom_options.php | 114 + ...ct_simple_with_custom_options_rollback.php | 26 + .../_files/product_simple_with_url_key.php | 2 +- .../Catalog/_files/product_simple_xss.php | 2 +- .../Catalog/_files/product_special_price.php | 2 +- .../_files/product_special_price_rollback.php | 2 +- .../Catalog/_files/product_text_attribute.php | 38 + .../Catalog/_files/product_virtual.php | 2 +- .../_files/product_virtual_in_stock.php | 2 +- .../product_virtual_in_stock_rollback.php | 2 +- .../_files/product_virtual_rollback.php | 2 +- .../_files/product_with_dropdown_option.php | 2 +- .../product_with_dropdown_option_rollback.php | 2 +- .../Catalog/_files/product_with_image.php | 2 +- .../_files/product_with_image_rollback.php | 2 +- .../Catalog/_files/product_with_options.php | 2 +- .../_files/product_with_options_rollback.php | 2 +- .../_files/product_with_two_websites.php | 22 +- .../product_with_two_websites_rollback.php | 35 + .../_files/product_without_options.php | 2 +- .../product_without_options_rollback.php | 2 +- .../Magento/Catalog/_files/products.php | 2 +- .../Catalog/_files/products_crosssell.php | 2 +- .../_files/products_crosssell_rollback.php | 2 +- .../Catalog/_files/products_for_search.php | 22 +- .../_files/products_for_search_rollback.php | 2 +- .../Catalog/_files/products_in_category.php | 2 +- .../_files/products_in_category_rollback.php | 2 +- .../Magento/Catalog/_files/products_new.php | 2 +- .../Catalog/_files/products_new_rollback.php | 2 +- .../Catalog/_files/products_related.php | 2 +- .../_files/products_related_multiple.php | 2 +- .../products_related_multiple_rollback.php | 2 +- .../_files/products_related_rollback.php | 2 +- .../Catalog/_files/products_rollback.php | 2 +- .../Catalog/_files/products_upsell.php | 2 +- .../_files/products_upsell_rollback.php | 2 +- .../products_with_multiselect_attribute.php | 79 +- ...ts_with_multiselect_attribute_rollback.php | 23 + .../products_with_unique_input_attribute.php | 2 +- .../quote_with_product_and_custom_options.php | 2 +- ...th_product_and_custom_options_rollback.php | 2 +- .../Magento/Catalog/_files/row_fixture.php | 2 +- .../Catalog/_files/row_fixture_rollback.php | 2 +- .../Catalog/_files/second_product_simple.php | 2 +- .../_files/second_product_simple_rollback.php | 2 +- .../Magento/Catalog/_files/second_website.php | 4 +- .../_files/second_website_rollback.php | 22 + .../_files/text_attribute_rollback.php | 18 + .../Catalog/_files/unique_input_attribute.php | 2 +- .../Magento/Catalog/_files/url_rewrites.php | 2 +- .../Catalog/_files/url_rewrites_invalid.php | 2 +- .../Catalog/_files/url_rewrites_rollback.php | 2 +- .../Magento/Catalog/_files/validate_image.php | 2 +- .../Catalog/_files/validate_image_info.php | 2 +- .../_files/validate_image_info_rollback.php | 2 +- .../_files/validate_image_rollback.php | 2 +- .../controllers/_files/attribute_system.php | 2 +- .../_files/attribute_system_popup.php | 2 +- .../attribute_system_with_applyto_data.php | 2 +- .../_files/attribute_user_defined.php | 2 +- .../Catalog/controllers/_files/products.php | 2 +- .../controllers/_files/products_rollback.php | 2 +- .../AbstractProductExportImportTestCase.php | 7 +- .../Model/Export/ProductTest.php | 81 +- .../Import/Product/Type/AbstractTest.php | 2 +- .../Model/Import/ProductTest.php | 339 +- .../_files/import_media_update_label.csv | 2 + .../Import/_files/media_import_image.php | 2 +- .../_files/media_import_image_rollback.php | 2 +- ...s_to_import_with_additional_attributes.csv | 3 + .../products_to_import_with_datetime.csv | 8 +- .../CatalogImportExport/Model/ProductTest.php | 2 +- .../_files/product_export_data.php | 37 +- .../_files/product_export_data_rollback.php | 10 + .../product_export_data_special_chars.php | 37 + ...uct_export_data_special_chars_rollback.php | 10 + .../_files/product_export_with_categories.php | 9 + ...product_export_with_product_links_data.php | 6 +- ...xport_with_product_links_data_rollback.php | 12 + .../Api/StockItemSaveTest.php | 43 + .../Form/Field/CustomergroupTest.php | 2 +- .../Model/Indexer/Stock/Action/FullTest.php | 2 +- .../Model/Indexer/Stock/Action/RowTest.php | 2 +- .../Model/Indexer/Stock/Action/RowsTest.php | 2 +- .../Quote/Item/QuantityValidatorTest.php | 2 +- .../Indexer/Stock/DefaultStockTest.php | 2 +- .../CatalogInventory/Model/Stock/ItemTest.php | 2 +- .../System/Config/Backend/MinsaleqtyTest.php | 122 + .../Model/Indexer/BatchIndexTest.php | 2 +- .../Model/Indexer/IndexerBuilderTest.php | 2 +- .../Model/Indexer/ProductRuleTest.php | 2 +- .../Model/Indexer/RuleProductTest.php | 2 +- .../ResourceModel/Rule/CollectionTest.php | 83 + .../Magento/CatalogRule/Model/RuleTest.php | 2 +- .../Magento/CatalogRule/_files/attribute.php | 2 +- .../_files/catalog_rule_10_off_not_logged.php | 2 +- .../CatalogRule/_files/rule_by_attribute.php | 2 +- .../Magento/CatalogRule/_files/two_rules.php | 2 +- .../Block/Advanced/ResultTest.php | 2 +- .../CatalogSearch/Block/ResultTest.php | 2 +- .../Magento/CatalogSearch/Block/TermTest.php | 2 +- .../CatalogSearch/Controller/AjaxTest.php | 2 +- .../CatalogSearch/Controller/ResultTest.php | 2 +- .../Magento/CatalogSearch/Helper/DataTest.php | 2 +- .../Model/Indexer/FulltextTest.php | 2 +- .../Model/Indexer/IndexSwitcherMock.php | 54 + .../Indexer/SwitcherUsedInFulltextTest.php | 214 + .../Model/Layer/Filter/AttributeTest.php | 2 +- .../Model/Layer/Filter/CategoryTest.php | 2 +- .../Model/Layer/Filter/DecimalTest.php | 2 +- .../Model/Layer/Filter/PriceTest.php | 2 +- .../ResourceModel/Advanced/CollectionTest.php | 2 +- .../ResourceModel/Fulltext/CollectionTest.php | 2 +- .../Model/Search/RequestGeneratorTest.php | 2 +- .../CatalogSearch/_files/full_reindex.php | 2 +- .../CatalogSearch/_files/indexer_fulltext.php | 2 +- .../_files/indexer_fulltext_rollback.php | 2 +- .../Magento/CatalogSearch/_files/query.php | 2 +- .../CatalogSearch/_files/query_redirect.php | 2 +- .../_files/search_attributes.php | 2 +- .../_files/search_attributes_rollback.php | 2 +- .../Model/CategoryUrlRewriteGeneratorTest.php | 40 +- ...oryProcessUrlRewriteSavingObserverTest.php | 188 + .../Adminhtml/Category/Tab/AttributesTest.php | 2 +- .../CatalogUrlRewrite/_files/categories.php | 2 +- .../_files/categories_rollback.php | 2 +- .../_files/categories_with_product_ids.php | 2 +- .../_files/categories_with_products.php | 2 +- .../categories_with_products_rollback.php | 2 +- .../_files/product_simple.php | 2 +- .../Block/Product/Widget/ConditionsTest.php | 2 +- .../Model/Rule/Condition/ProductTest.php | 2 +- .../Magento/Checkout/Block/Cart/GridTest.php | 31 + .../Magento/Checkout/Block/CartTest.php | 14 +- .../Controller/Cart/Index/CouponPostTest.php | 39 + .../Magento/Checkout/Controller/CartTest.php | 49 +- .../Magento/Checkout/Model/CartTest.php | 2 +- .../Magento/Checkout/Model/SessionTest.php | 2 +- .../Magento/Checkout/_files/active_quote.php | 2 +- .../Checkout/_files/active_quote_rollback.php | 2 +- .../Magento/Checkout/_files/cart.php | 2 +- .../Checkout/_files/discount_10percent.php | 2 +- .../discount_10percent_generalusers.php | 2 +- ...scount_10percent_generalusers_rollback.php | 2 +- .../_files/discount_10percent_rollback.php | 2 +- .../Checkout/_files/product_bundle.php | 2 +- .../_files/product_with_custom_option.php | 2 +- .../Magento/Checkout/_files/quote.php | 2 +- .../Checkout/_files/quote_with_address.php | 2 +- .../_files/quote_with_address_rollback.php | 2 +- .../_files/quote_with_address_saved.php | 8 +- .../quote_with_address_saved_rollback.php | 2 +- .../_files/quote_with_bundle_and_options.php | 2 +- ...quote_with_bundle_and_options_rollback.php | 2 +- .../_files/quote_with_bundle_product.php | 2 +- .../_files/quote_with_check_payment.php | 2 +- .../quote_with_check_payment_rollback.php | 2 +- .../_files/quote_with_coupon_saved.php | 2 +- .../quote_with_coupon_saved_rollback.php | 2 +- .../quote_with_downloadable_product.php | 2 +- .../_files/quote_with_items_saved.php | 2 +- .../quote_with_items_saved_rollback.php | 2 +- .../_files/quote_with_payment_saved.php | 7 +- .../quote_with_payment_saved_rollback.php | 2 +- .../_files/quote_with_product_and_payment.php | 2 +- .../_files/quote_with_shipping_method.php | 2 +- ...h_shipping_method_and_items_categories.php | 4 +- .../quote_with_shipping_method_rollback.php | 2 +- .../_files/quote_with_simple_product.php | 2 +- ..._with_simple_product_and_custom_option.php | 2 +- .../quote_with_simple_product_and_image.php | 2 +- ...with_simple_product_and_image_rollback.php | 2 +- .../quote_with_simple_product_saved.php | 2 +- ...ote_with_simple_product_saved_rollback.php | 2 +- ...quote_with_virtual_product_and_address.php | 2 +- ...h_virtual_product_and_address_rollback.php | 2 +- .../quote_with_virtual_product_saved.php | 2 +- ...te_with_virtual_product_saved_rollback.php | 2 +- .../_files/set_product_min_in_cart.php | 2 +- .../Checkout/_files/simple_product.php | 2 +- .../agreement_active_with_html_content.php | 2 +- ...ment_active_with_html_content_rollback.php | 2 +- .../agreement_inactive_with_text_content.php | 2 +- ...nt_inactive_with_text_content_rollback.php | 2 +- .../testsuite/Magento/Cms/Block/BlockTest.php | 2 +- .../testsuite/Magento/Cms/Block/PageTest.php | 2 +- .../Magento/Cms/Block/Widget/BlockTest.php | 2 +- .../Adminhtml/Wysiwyg/Images/IndexTest.php | 2 +- .../Cms/Controller/Noroute/IndexTest.php | 2 +- .../Magento/Cms/Controller/PageTest.php | 2 +- .../Magento/Cms/Controller/RouterTest.php | 2 +- .../testsuite/Magento/Cms/Helper/PageTest.php | 2 +- .../Magento/Cms/Helper/Wysiwyg/ImagesTest.php | 2 +- .../testsuite/Magento/Cms/Model/PageTest.php | 20 +- .../Magento/Cms/Model/Wysiwyg/ConfigTest.php | 2 +- .../Cms/Model/Wysiwyg/Images/StorageTest.php | 2 +- .../testsuite/Magento/Cms/_files/block.php | 2 +- .../testsuite/Magento/Cms/_files/noroute.php | 2 +- .../testsuite/Magento/Cms/_files/pages.php | 2 +- .../Config/Block/System/Config/FormStub.php | 2 +- .../Config/Block/System/Config/FormTest.php | 2 +- .../Config/_files/test_section_config.xml | 2 +- .../Console/Command/ConfigSetCommandTest.php | 421 + .../Console/Command/ConfigShowCommandTest.php | 312 + .../Adminhtml/System/ConfigTest.php | 2 +- .../Model/Config/Backend/Admin/RobotsTest.php | 2 +- .../Model/Config/Backend/BaseurlTest.php | 13 +- .../Model/Config/Backend/EncryptedTest.php | 2 +- .../Config/Backend/Image/AdapterTest.php | 2 +- .../Processor/EnvironmentPlaceholderTest.php | 103 + .../Magento/Config/Model/ConfigTest.php | 2 +- .../Config/Model/ResourceModel/ConfigTest.php | 2 +- .../Config/Model/_files/config_groups.php | 2 +- .../Config/Model/_files/config_section.php | 2 +- .../Config/Model/_files/no_robots_txt.php | 2 +- .../Config/Model/_files/robots_txt.php | 2 +- .../Magento/Config/_files/_config.local.php | 46 + .../Magento/Config/_files/_config.php | 37 + .../Magento/Config/_files/config_data.php | 61 + .../Model/ConfigurableTest.php | 11 +- .../Model/Export/RowCustomizerTest.php | 2 +- .../Import/Product/Type/ConfigurableTest.php | 48 +- .../_files/import_configurable_12345.csv | 4 + .../Edit/Tab/Variations/Config/MatrixTest.php | 2 +- .../Product/View/Type/ConfigurableTest.php | 2 +- .../Controller/Adminhtml/ProductTest.php | 2 +- .../Controller/CartTest.php | 2 +- .../Model/OptionRepositoryTest.php | 3 +- .../Type/Configurable/AttributeTest.php | 2 +- .../Product/Type/Configurable/PriceTest.php | 6 +- .../Model/Product/Type/ConfigurableTest.php | 20 +- .../Model/Product/VariationHandlerTest.php | 2 +- .../Indexer/Price/ConfigurableTest.php | 106 + .../Price/LowestPriceOptionProviderTest.php | 158 + .../Pricing/Price/SpecialPriceIndexerTest.php | 101 + .../Pricing/Price/SpecialPriceTest.php | 112 + .../Modifier/Data/AssociatedProductsTest.php | 2 +- .../_files/configurable_attribute.php | 7 +- .../configurable_attribute_rollback.php | 2 +- .../_files/delete_association.php | 2 +- ...der_item_with_configurable_and_options.php | 2 +- ...with_configurable_and_options_rollback.php | 2 +- .../_files/product_configurable.php | 2 +- .../_files/product_configurable_12345.php | 133 + .../product_configurable_12345_rollback.php | 36 + .../_files/product_configurable_rollback.php | 2 +- .../_files/product_simple_77.php | 15 +- .../quote_with_configurable_product.php | 2 +- ...ote_with_configurable_product_rollback.php | 2 +- .../ConfigurableProduct/_files/tax_rule.php | 2 +- .../etc/extension_attributes.xml | 2 +- .../Magento/Contact/Controller/IndexTest.php | 52 +- .../Magento/Contact/Helper/DataTest.php | 2 +- .../Magento/Contact/Model/ConfigTest.php | 42 + .../Model/Config/Backend/DomainTest.php | 2 +- .../Model/Config/Backend/LifetimeTest.php | 2 +- .../Cookie/Model/Config/Backend/PathTest.php | 2 +- .../Observer/ProcessCronQueueObserverTest.php | 2 +- .../System/Currency/FetchRatesTest.php | 2 +- .../Adminhtml/System/Currency/IndexTest.php | 2 +- .../System/Currency/SaveRatesTest.php | 2 +- .../System/Currencysymbol/IndexTest.php | 2 +- .../System/Currencysymbol/SaveTest.php | 2 +- .../Model/System/CurrencysymbolTest.php | 2 +- .../Customer/Api/AddressRepositoryTest.php | 2 +- .../Block/Account/Dashboard/AddressTest.php | 2 +- .../Block/Account/Dashboard/InfoTest.php | 2 +- .../Customer/Block/Account/DashboardTest.php | 2 +- .../Customer/Block/Address/BookTest.php | 2 +- .../Customer/Block/Address/EditTest.php | 2 +- .../Address/Renderer/DefaultRendererTest.php | 6 +- .../Block/Adminhtml/Edit/Tab/CartTest.php | 2 +- .../Block/Adminhtml/Edit/Tab/CartsTest.php | 2 +- .../Adminhtml/Edit/Tab/NewsletterTest.php | 2 +- .../Block/Adminhtml/Edit/Tab/OrdersTest.php | 2 +- .../Adminhtml/Edit/Tab/View/CartTest.php | 2 +- .../Edit/Tab/View/PersonalInfoTest.php | 2 +- .../Adminhtml/Edit/Tab/View/SalesTest.php | 2 +- .../Block/Adminhtml/Edit/Tab/ViewTest.php | 2 +- .../Customer/Block/Adminhtml/EditTest.php | 2 +- .../Block/Adminhtml/Group/Edit/FormTest.php | 2 +- .../Block/Adminhtml/Group/EditTest.php | 2 +- .../Customer/Block/Form/RegisterTest.php | 132 + .../Customer/Block/Widget/CompanyTest.php | 57 + .../Magento/Customer/Block/Widget/DobTest.php | 2 +- .../Magento/Customer/Block/Widget/FaxTest.php | 57 + .../Customer/Block/Widget/GenderTest.php | 2 +- .../Customer/Block/Widget/NameTest.php | 2 +- .../Customer/Block/Widget/TaxvatTest.php | 2 +- .../Customer/Block/Widget/TelephoneTest.php | 57 + .../Customer/Controller/AccountTest.php | 2 +- .../Customer/Controller/AddressTest.php | 2 +- .../Cart/Product/Composite/CartTest.php | 2 +- .../Controller/Adminhtml/GroupTest.php | 2 +- .../Adminhtml/Index/MassAssignGroupTest.php | 2 +- .../Adminhtml/Index/MassDeleteTest.php | 2 +- .../Adminhtml/Index/MassSubscribeTest.php | 2 +- .../Adminhtml/Index/ResetPasswordTest.php | 88 + .../Controller/Adminhtml/IndexTest.php | 4 +- .../Customer/Controller/AjaxLoginTest.php | 2 +- .../Customer/Controller/Section/LoadTest.php | 19 + .../Magento/Customer/Helper/AddressTest.php | 2 +- .../Magento/Customer/Helper/ViewTest.php | 2 +- .../Customer/Model/AccountManagementTest.php | 2 +- .../Customer/Model/AddressMetadataTest.php | 191 +- .../Customer/Model/AddressRegistryTest.php | 2 +- .../Magento/Customer/Model/AddressTest.php | 2 +- .../Customer/Model/Config/ShareTest.php | 2 +- .../Config/Source/Group/MultiselectTest.php | 21 +- .../Model/Config/Source/GroupTest.php | 2 +- .../Customer/Model/CustomerMetadataTest.php | 224 +- .../Customer/Model/CustomerRegistryTest.php | 2 +- .../Magento/Customer/Model/CustomerTest.php | 2 +- .../Customer/Model/FileResolverStub.php | 2 +- .../Magento/Customer/Model/FormTest.php | 2 +- .../Customer/Model/GroupManagementTest.php | 2 +- .../Customer/Model/GroupRegistryTest.php | 2 +- .../Magento/Customer/Model/GroupTest.php | 2 +- .../Model/Metadata/FormFactoryTest.php | 2 +- .../Customer/Model/Metadata/FormTest.php | 2 +- .../ResourceModel/Address/CollectionTest.php | 2 +- .../ResourceModel/AddressRepositoryTest.php | 2 +- .../ResourceModel/Customer/CollectionTest.php | 2 +- .../ResourceModel/CustomerRepositoryTest.php | 40 +- .../Group/Grid/ServiceCollectionTest.php | 2 +- .../ResourceModel/GroupRepositoryTest.php | 2 +- .../Magento/Customer/Model/SessionTest.php | 2 +- .../Magento/Customer/Model/VisitorTest.php | 2 +- .../_files/attribute_user_defined_address.php | 2 +- ..._user_defined_address_custom_attribute.php | 2 +- ...ined_address_custom_attribute_rollback.php | 2 +- ...ttribute_user_defined_address_rollback.php | 2 +- ...ttribute_user_defined_custom_attribute.php | 2 +- ...user_defined_custom_attribute_rollback.php | 2 +- .../attribute_user_defined_customer.php | 2 +- ...tribute_user_defined_customer_rollback.php | 2 +- .../_files/attribute_user_fullname.php | 2 +- .../Magento/Customer/_files/customer.php | 2 +- .../Customer/_files/customer_address.php | 2 +- .../_files/customer_address_rollback.php | 2 +- .../_files/customer_from_repository.php | 2 +- .../Customer/_files/customer_group.php | 2 +- .../Customer/_files/customer_no_address.php | 2 +- .../customer_non_default_website_id.php | 16 +- ...stomer_non_default_website_id_rollback.php | 2 +- .../_files/customer_primary_addresses.php | 2 +- .../Customer/_files/customer_rollback.php | 2 +- .../Customer/_files/customer_rp_token.php | 2 +- .../Customer/_files/customer_sample.php | 2 +- .../_files/customer_two_addresses.php | 2 +- .../Customer/_files/customer_with_website.php | 27 + .../_files/etc/extension_attributes.xml | 2 +- .../_files/import_export/customer.php | 2 +- .../import_export/customer_with_addresses.php | 2 +- .../_files/import_export/customers.php | 2 +- .../customers_for_address_import.php | 2 +- .../Customer/_files/inactive_customer.php | 2 +- .../Magento/Customer/_files/quote.php | 2 +- .../Magento/Customer/_files/sales_order.php | 2 +- .../Customer/_files/three_customers.php | 2 +- .../Magento/Customer/_files/two_customers.php | 2 +- .../Customer/etc/extension_attributes.xml | 2 +- .../Model/Export/AddressTest.php | 2 +- .../Model/Export/CustomerTest.php | 2 +- .../Model/Import/AddressTest.php | 2 +- .../Model/Import/CustomerCompositeTest.php | 2 +- .../Model/Import/CustomerTest.php | 2 +- .../Import/_files/customers_to_import.csv | 6 +- .../App/ApplicationDumpCommandTest.php | 169 + .../App/SensitiveConfigSetCommandTest.php | 297 + .../Magento/Deploy/_files/_config.local.php | 23 + .../Magento/Deploy/_files/config_data.php | 33 + .../Command/SourceThemeDeployCommandTest.php | 2 +- .../Magento/Developer/Helper/DataTest.php | 2 +- .../Model/Config/Backend/AllowedIpsTest.php | 2 +- .../Dhl/Block/Adminhtml/UnitofmeasureTest.php | 2 +- .../Magento/Directory/Block/DataTest.php | 38 + .../Magento/Directory/Helper/DataTest.php | 2 +- .../Country/Postcode/Config/ReaderTest.php | 43 + .../Model/Country/Postcode/ValidatorTest.php | 2 +- .../Magento/Directory/Model/ObserverTest.php | 6 +- .../Edit/Tab/Downloadable/LinksTest.php | 2 +- .../Edit/Tab/Downloadable/SamplesTest.php | 2 +- .../Adminhtml/Downloadable/FileTest.php | 2 +- .../Downloadable/Controller/ProductTest.php | 2 +- .../Downloadable/Model/Product/TypeTest.php | 43 +- ...der_item_with_downloadable_and_options.php | 2 +- ...with_downloadable_and_options_rollback.php | 2 +- .../order_with_downloadable_product.php | 2 +- ...adable_product_with_additional_options.php | 61 + .../_files/product_downloadable.php | 2 +- .../_files/product_downloadable_rollback.php | 2 +- .../product_downloadable_with_files.php | 2 +- ...oduct_downloadable_with_files_rollback.php | 2 +- .../quote_with_downloadable_product.php | 2 +- ...ote_with_downloadable_product_rollback.php | 2 +- .../Downloadable/etc/extension_attributes.xml | 2 +- .../Model/DownloadableTest.php | 15 +- .../Import/Product/Type/DownloadableTest.php | 4 +- .../Attribute/Edit/Main/AbstractMainTest.php | 2 +- .../Model/Attribute/GroupRepositoryTest.php | 2 +- .../Eav/Model/AttributeManagementTest.php | 76 + .../Eav/Model/AttributeRepositoryTest.php | 2 +- .../Magento/Eav/Model/ConfigTest.php | 40 + .../Entity/Attribute/CollectionTest.php | 2 +- .../Model/ResourceModel/UpdateHandlerTest.php | 256 + .../Model/Validator/Attribute/BackendTest.php | 2 +- .../Eav/_files/attribute_for_search.php | 2 +- .../Eav/_files/attribute_group_for_search.php | 2 +- .../Eav/_files/attribute_set_for_search.php | 2 +- .../attribute_set_for_search_rollback.php | 2 +- .../Eav/_files/empty_attribute_set.php | 2 +- .../_files/empty_attribute_set_rollback.php | 2 +- .../Adminhtml/Template/Edit/FormTest.php | 2 +- .../Adminhtml/Email/TemplateTest.php | 2 +- .../Email/Model/Template/FilterTest.php | 3 +- .../Magento/Email/Model/TemplateTest.php | 2 +- .../code/Magento/Email/view/email/footer.html | 2 +- .../code/Magento/Email/view/email/header.html | 2 +- .../layout/email_template_test_handle.xml | 2 +- .../templates/sample_email_content.phtml | 2 +- .../email/cron_error.html | 2 +- .../Magento/default/registration.php | 2 +- .../adminhtml/Magento/default/theme.xml | 2 +- .../Vendor/custom_theme/registration.php | 2 +- .../adminhtml/Vendor/custom_theme/theme.xml | 2 +- .../adminhtml/Vendor/default/registration.php | 2 +- .../design/adminhtml/Vendor/default/theme.xml | 2 +- .../email/account_new_confirmed.html | 2 +- .../layout/email_template_test_handle.xml | 2 +- .../templates/sample_email_content.phtml | 2 +- .../sample_email_content_custom.phtml | 2 +- .../frontend/Magento/default/registration.php | 2 +- .../design/frontend/Magento/default/theme.xml | 2 +- .../Magento/default/web/css/email-3.less | 2 +- .../default/web/css/email-inline-3.less | 2 +- .../default/web/css/file-with-error.less | 2 +- .../Magento_Customer/email/account_new.html | 2 +- .../Vendor/custom_theme/registration.php | 2 +- .../frontend/Vendor/custom_theme/theme.xml | 2 +- .../Vendor/custom_theme/web/css/email-1.less | 2 +- .../custom_theme/web/css/email-inline-1.less | 2 +- .../email/account_new_confirmation.html | 2 +- .../frontend/Vendor/default/registration.php | 2 +- .../design/frontend/Vendor/default/theme.xml | 2 +- .../Vendor/default/web/css/email-2.less | 2 +- .../default/web/css/email-inline-2.less | 2 +- .../Email/Model/_files/email_template.php | 2 +- .../Block/Adminhtml/Crypt/Key/EditTest.php | 2 +- .../Block/Adminhtml/Crypt/Key/FormTest.php | 2 +- .../Adminhtml/Crypt/Key/IndexTest.php | 2 +- .../Adminhtml/Crypt/Key/SaveTest.php | 2 +- .../Model/ResourceModel/Key/ChangeTest.php | 4 +- .../EncryptionKey/_files/payment_info.php | 2 +- .../Magento/Fedex/Model/CarrierTest.php | 2 +- .../Fedex/Model/Source/UnitofmeasureTest.php | 2 +- .../Api/AbstractExtensibleObjectTest.php | 2 +- .../ExtensionAttribute/Config/ReaderTest.php | 2 +- .../Config/_files/config_one.xml | 2 +- .../Config/_files/config_two.xml | 2 +- .../ExtensionAttribute/JoinProcessorTest.php | 2 +- .../Api/ExtensionAttributesFactoryTest.php | 2 +- .../Api/Data/FakeAddressInterface.php | 2 +- .../Api/Data/FakeExtensibleOneInterface.php | 2 +- .../Api/Data/FakeExtensibleTwoInterface.php | 2 +- .../Api/Data/FakeRegionInterface.php | 2 +- .../Wonderland/Model/Data/FakeAddress.php | 2 +- .../Model/Data/FakeExtensibleOne.php | 2 +- .../Model/Data/FakeExtensibleTwo.php | 2 +- .../Wonderland/Model/Data/FakeRegion.php | 2 +- .../Magento/Wonderland/Model/FakeAddress.php | 2 +- .../Magento/Wonderland/Model/FakeRegion.php | 2 +- .../Api/_files/extension_attributes.xml | 2 +- .../Api/etc/extension_attributes.xml | 2 +- .../Magento/Framework/App/AreaTest.php | 10 +- .../Magento/Framework/App/Config/BaseTest.php | 2 +- .../Magento/Framework/App/Config/DataTest.php | 7 +- .../Framework/App/Config/InitialTest.php | 55 + .../Framework/App/FrontControllerTest.php | 2 +- .../Framework/App/Language/DictionaryTest.php | 2 +- .../Language/_files/bar/en_gb/language.xml | 2 +- .../_files/bar/en_gb/registration.php | 2 +- .../Language/_files/bar/en_us/language.xml | 2 +- .../_files/bar/en_us/registration.php | 2 +- .../Language/_files/baz/en_gb/language.xml | 2 +- .../_files/baz/en_gb/registration.php | 2 +- .../Language/_files/first/en_us/language.xml | 2 +- .../_files/first/en_us/registration.php | 2 +- .../Language/_files/foo/en_au/language.xml | 2 +- .../_files/foo/en_au/registration.php | 2 +- .../App/Language/_files/my/ru_ru/language.xml | 2 +- .../Language/_files/my/ru_ru/registration.php | 2 +- .../Language/_files/second/en_gb/language.xml | 2 +- .../_files/second/en_gb/registration.php | 2 +- .../Language/_files/theirs/ru_ru/language.xml | 2 +- .../_files/theirs/ru_ru/registration.php | 2 +- .../App/ObjectManager/ConfigLoaderTest.php | 33 + .../ConnectionFactoryTest.php | 2 +- .../HeaderProvider/AbstractHeaderTestCase.php | 2 +- .../App/Response/HeaderProvider/HstsTest.php | 2 +- .../HeaderProvider/UpgradeInsecureTest.php | 2 +- .../HeaderProvider/XFrameOptionsTest.php | 2 +- .../Framework/App/Route/ConfigTest.php | 45 + .../Magento/Framework/App/Router/BaseTest.php | 2 +- .../Framework/App/Utility/FilesTest.php | 2 +- .../_files/fixtures/language/registration.php | 2 +- .../_files/fixtures/library/registration.php | 2 +- .../_files/fixtures/module/registration.php | 2 +- .../_files/fixtures/theme/registration.php | 2 +- .../App/View/Deployment/VersionTest.php | 105 + .../Framework/Backup/FilesystemTest.php | 50 + .../backups/1474538269_filesystem_code.tgz | Bin 0 -> 336 bytes .../Framework/Cache/Backend/MongoDbTest.php | 2 +- .../Magento/Framework/Cache/CoreTest.php | 2 +- .../Magento/Framework/Code/GeneratorTest.php | 2 +- .../ParentClassWithNamespace.php | 5 +- .../SourceClassWithNamespace.php | 5 +- .../Code/Reader/SourceArgumentsReaderTest.php | 2 +- .../SourceArgumentsReaderTest.php.sample | 4 +- ...SourceClassWithNamespaceFactory.php.sample | 2 +- ...ceClassWithNamespaceInterceptor.php.sample | 2 +- .../SourceClassWithNamespaceProxy.php.sample | 2 +- .../Framework/Code/_files/ClassToFind.php | 2 +- .../Framework/Communication/ConfigTest.php | 38 +- ...nication_incorrect_request_schema_type.php | 2 +- .../communication_invalid_topic_name.php | 2 +- ...nication_is_synchronous_is_not_boolean.php | 2 +- .../_files/communication_missing_handler.xml | 2 +- .../_files/communication_missing_request.xml | 2 +- ...ion_multiple_handlers_synchronous_mode.php | 2 +- ...ion_multiple_handlers_synchronous_mode.xml | 2 +- .../_files/communication_no_attributes.xml | 2 +- ...munication_not_existing_handler_method.php | 2 +- ...munication_not_existing_handler_method.xml | 2 +- .../communication_not_existing_service.xml | 2 +- ...munication_not_existing_service_method.xml | 2 +- ...unication_request_not_existing_service.php | 2 +- ...unication_request_not_existing_service.xml | 2 +- ...nication_response_not_existing_service.php | 2 +- ...nication_response_not_existing_service.xml | 2 +- ...ommunication_topic_with_excessive_keys.php | 2 +- .../communication_topic_with_missed_keys.php | 2 +- .../communication_topic_without_data.php | 2 +- .../communication_with_disabled_handler.php | 2 +- .../communication_with_non_matched_name.php | 2 +- .../_files/invalid_communication_numeric.xml | 12 + .../_files/valid_communication.xml | 2 +- .../_files/valid_communication_expected.php | 2 +- .../_files/valid_communication_input.php | 2 +- .../_files/valid_communication_numeric.xml | 12 + .../Composer/ComposerInformationTest.php | 2 +- .../Magento/Framework/Composer/RemoveTest.php | 2 +- .../_files/testFromClone/vendor/README | 2 +- .../testFromCreateProject/vendor/README | 2 +- .../_files/testSkeleton/vendor/README | 2 +- .../Framework/Composer/_files/vendor_path.php | 2 +- .../File/Collector/AggregatedTest.php | 2 +- .../code/Magento/Other/registration.php | 2 +- .../Magento/Other/view/frontend/web/3.less | 2 +- .../code/Magento/Third/registration.php | 2 +- .../Magento/Third/view/frontend/web/3.less | 2 +- .../MagentoFrameworkCssTest_Third/web/3.less | 2 +- .../frontend/Test/default/registration.php | 2 +- .../design/frontend/Test/default/theme.xml | 2 +- .../frontend/Test/parent/registration.php | 2 +- .../design/frontend/Test/parent/theme.xml | 2 +- .../Css/PreProcessor/_files/lib/web/3.less | 2 +- .../Framework/DB/Adapter/InterfaceTest.php | 2 +- .../Framework/DB/Adapter/Pdo/MysqlTest.php | 8 +- .../DB/DataConverter/DataConverterTest.php | 184 + .../Magento/Framework/DB/HelperTest.php | 2 +- .../Magento/Framework/DB/TransactionTest.php | 2 +- .../Argument/Interpreter/StringUtilsTest.php | 2 +- .../Framework/Data/Form/Element/DateTest.php | 2 +- .../Data/Form/Element/FieldsetTest.php | 2 +- .../Framework/Data/Form/Element/ImageTest.php | 2 +- .../DataObject/Copy/Config/ReaderTest.php | 2 +- .../Copy/Config/_files/expectedArray.php | 2 +- .../Copy/Config/_files/fieldset.xml | 2 +- .../Config/_files/partialFieldsetFirst.xml | 2 +- .../Config/_files/partialFieldsetSecond.xml | 2 +- .../Magento/Framework/DataObject/CopyTest.php | 2 +- .../Data/FakeAttributeMetadataInterface.php | 2 +- .../Api/Data/FakeCustomerInterface.php | 2 +- .../Model/Data/FakeAttributeMetadata.php | 2 +- .../Wonderland/Model/Data/FakeCustomer.php | 2 +- .../Model/FakeAttributeMetadata.php | 2 +- .../Magento/Wonderland/Model/FakeCustomer.php | 2 +- .../Framework/Encryption/EncryptorTest.php | 2 +- .../Framework/Encryption/ModelTest.php | 2 +- .../Exception/NoSuchEntityExceptionTest.php | 2 +- .../Magento/Framework/File/SizeTest.php | 2 +- .../Filesystem/Directory/ReadTest.php | 2 +- .../Filesystem/Directory/WriteTest.php | 2 +- .../Framework/Filesystem/Driver/FileTest.php | 2 +- .../Framework/Filesystem/File/ReadTest.php | 2 +- .../Framework/Filesystem/File/WriteTest.php | 2 +- .../Framework/Filesystem/FileResolverTest.php | 2 +- .../Framework/Filesystem/FilesystemTest.php | 2 +- .../Filesystem/_files/ClassToFind.php | 2 +- .../Template/Tokenizer/ParameterTest.php | 2 +- .../Magento/Framework/HTTP/HeaderTest.php | 2 +- .../HTTP/PhpEnvironment/RemoteAddressTest.php | 2 +- .../HTTP/PhpEnvironment/ServerAddressTest.php | 2 +- .../Framework/Image/Adapter/ConfigTest.php | 2 +- .../Framework/Image/Adapter/InterfaceTest.php | 7 +- .../Framework/Interception/AbstractPlugin.php | 6 +- .../Interception/Fixture/Intercepted.php | 3 +- .../Fixture/Intercepted/FirstPlugin.php | 2 +- .../Fixture/Intercepted/InterfacePlugin.php | 2 +- .../Fixture/Intercepted/Plugin.php | 2 +- .../Fixture/InterceptedInterface.php | 2 +- .../Fixture/InterceptedParent.php | 2 +- .../Fixture/InterceptedParentInterface.php | 2 +- .../Framework/Interception/GeneralTest.php | 2 +- .../Framework/Interception/TwoPluginTest.php | 2 +- .../Framework/Json/Helper/DataTest.php | 2 +- .../Message/CollectionFactoryTest.php | 2 +- .../Magento/Framework/Message/FactoryTest.php | 2 +- .../Magento/Framework/Message/ManagerTest.php | 2 +- .../Framework/Model/Entity/HydratorTest.php | 2 +- .../Model/ResourceModel/Db/AbstractTest.php | 2 +- .../Db/Collection/AbstractTest.php | 2 +- .../Model/ResourceModel/Db/ProfilerTest.php | 2 +- .../Model/ResourceModel/Entity/TableTest.php | 2 +- .../Model/ResourceModel/IteratorTest.php | 2 +- .../Type/Db/ConnectionFactoryTest.php | 2 +- .../ResourceModel/Type/Db/Pdo/MysqlTest.php | 2 +- .../Magento/Framework/Model/ResourceTest.php | 2 +- .../Module/Plugin/DbStatusValidatorTest.php | 2 +- .../Framework/Mview/View/ChangelogTest.php | 2 +- .../ObjectManager/Config/Reader/DomTest.php | 2 +- ...ractFactoryRuntimeDefinitionsTestCases.php | 130 + .../ObjectManager/Factory/CompiledTest.php | 53 + .../Factory/Dynamic/DeveloperTest.php | 55 + .../ObjectManager/ObjectManagerTest.php | 2 +- .../ObjectManager/TestAsset/Basic.php | 2 +- .../ObjectManager/TestAsset/BasicAlias.php | 10 - .../TestAsset/BasicInjection.php | 7 +- .../TestAsset/ComplexDependencies.php | 127 + .../TestAsset/ConstructorEightArguments.php | 2 +- .../TestAsset/ConstructorFiveArguments.php | 2 +- .../TestAsset/ConstructorFourArguments.php | 2 +- .../TestAsset/ConstructorNineArguments.php | 2 +- .../TestAsset/ConstructorNoArguments.php | 2 +- .../TestAsset/ConstructorOneArgument.php | 14 +- .../TestAsset/ConstructorSevenArguments.php | 2 +- .../TestAsset/ConstructorSixArguments.php | 2 +- .../TestAsset/ConstructorTenArguments.php | 2 +- .../TestAsset/ConstructorThreeArguments.php | 2 +- .../TestAsset/ConstructorTwoArguments.php | 2 +- .../TestAsset/DependsOnAlias.php | 36 + .../TestAsset/DependsOnInterface.php | 30 + .../TestAsset/HasOptionalParameters.php | 114 + .../TestAsset/InterfaceImplementation.php | 2 +- .../TestAsset/InterfaceInjection.php | 2 +- .../TestAsset/TestAssetInterface.php | 2 +- .../ObjectManager/_files/config_merged.xml | 2 +- .../ObjectManager/_files/config_one.xml | 2 +- .../ObjectManager/_files/config_two.xml | 2 +- .../Framework/Pricing/Helper/DataTest.php | 2 +- .../Driver/Standard/Output/CsvfileTest.php | 2 +- .../Driver/Standard/Output/FirebugTest.php | 2 +- .../Driver/Standard/Output/HtmlTest.php | 2 +- .../Driver/Standard/Output/_files/output.html | 2 +- .../Driver/Standard/Output/_files/timers.php | 2 +- .../Magento/Framework/ProfilerTest.php | 2 +- .../Framework/Reflection/MethodsMapTest.php | 48 + .../Search/Adapter/Mysql/AdapterTest.php | 137 +- .../Adapter/Mysql/Builder/Query/MatchTest.php | 2 +- .../Search/Request/Config/ConverterTest.php | 2 +- .../Request/Config/FileResolverStub.php | 2 +- .../Request/Config/FileSystemReaderTest.php | 2 +- .../Framework/Search/Request/MapperTest.php | 2 +- .../Search/_files/configurable_attribute.php | 61 + .../configurable_attribute_rollback.php | 28 + .../Search/_files/date_attribute.php | 2 +- .../Search/_files/date_attribute_rollback.php | 2 +- .../Search/_files/etc/search_request_1.xml | 2 +- .../Search/_files/etc/search_request_2.xml | 2 +- .../Search/_files/filterable_attribute.php | 2 +- .../_files/filterable_attribute_rollback.php | 3 +- .../Search/_files/product_configurable.php | 121 + .../_files/product_configurable_rollback.php | 36 + ...oduct_configurable_with_disabled_child.php | 18 + ...figurable_with_disabled_child_rollback.php | 7 + .../Framework/Search/_files/products.php | 2 +- .../Search/_files/products_rollback.php | 2 +- .../Framework/Search/_files/requests.xml | 53 +- .../Search/_files/search_request.xml | 4 +- .../Search/_files/search_request_config.php | 2 +- .../Search/_files/search_request_merged.php | 2 +- .../Search/_files/search_weight_products.php | 90 + .../search_weight_products_rollback.php | 37 + .../Validator/CookieDomainValidatorTest.php | 2 +- .../Validator/CookieLifetimeValidatorTest.php | 2 +- .../Validator/CookiePathValidatorTest.php | 2 +- .../Magento/Framework/Session/ConfigTest.php | 2 +- .../Session/SaveHandler/DbTableTest.php | 2 +- .../Framework/Session/SaveHandlerTest.php | 47 +- .../Framework/Session/SessionManagerTest.php | 2 +- .../Framework/Session/SidResolverTest.php | 2 +- .../Stdlib/Cookie/CookieScopeTest.php | 2 +- .../Stdlib/Cookie/PhpCookieManagerTest.php | 2 +- .../Stdlib/Cookie/PhpCookieReaderTest.php | 2 +- .../Framework/Translate/InlineTest.php | 2 +- .../_files/_inline_page_expected.html | 2 +- .../_files/_inline_page_original.html | 2 +- .../Translate/_files/_translation_data.php | 2 +- .../Framework/TranslateCachingTest.php | 8 +- .../Magento/Framework/TranslateTest.php | 28 +- .../Magento/Framework/Url/Helper/DataTest.php | 2 +- .../testsuite/Magento/Framework/UrlTest.php | 2 +- .../Framework/Validator/FactoryTest.php | 2 +- .../Framework/ValidatorFactoryTest.php | 2 +- .../Framework/View/Asset/MinifierTest.php | 2 +- .../View/Design/Fallback/RulePoolTest.php | 2 +- .../Design/FileResolution/FallbackTest.php | 2 +- .../Framework/View/Design/Theme/LabelTest.php | 2 +- .../View/Design/Theme/ValidatorTest.php | 2 +- .../View/Element/AbstractBlockTest.php | 2 +- .../Framework/View/Element/TemplateTest.php | 2 +- .../Framework/View/Element/Text/ListTest.php | 2 +- .../Framework/View/Element/TextTest.php | 2 +- .../Config/Provider/TemplateTest.php | 79 + .../frontend/Magento/plushe/css/wrong.css | 2 +- .../Magento/Framework/View/FileSystemTest.php | 2 +- .../View/Fixture/Block/BrokenAction.php | 2 +- .../View/Fixture/Block/BrokenConstructor.php | 2 +- .../View/Fixture/Block/BrokenLayout.php | 2 +- .../Framework/View/Layout/ElementTest.php | 2 +- .../Framework/View/Layout/MergeTest.php | 2 +- .../View/Layout/Reader/BlockTest.php | 2 +- .../Reader/_files/_layout_update_block.xml | 2 +- .../_files/_layout_update_reference.xml | 2 +- .../View/Layout/_files/_layout_update.xml | 2 +- .../layout/catalog_category_default.xml | 2 +- .../layout/catalog_category_layered.xml | 2 +- .../layout/catalog_product_view.xml | 2 +- ...catalog_product_view_type_configurable.xml | 2 +- .../catalog_product_view_type_simple.xml | 2 +- .../layout/checkout_index_index.xml | 2 +- .../_mergeFiles/layout/customer_account.xml | 2 +- .../Layout/_mergeFiles/layout/default.xml | 2 +- .../Layout/_mergeFiles/layout/file_wrong.xml | 2 +- .../_mergeFiles/layout/fixture_handle_one.xml | 2 +- .../layout/fixture_handle_page_layout.xml | 2 +- .../_mergeFiles/layout/fixture_handle_two.xml | 2 +- .../fixture_handle_with_page_layout.xml | 2 +- .../_mergeFiles/layout/not_a_page_type.xml | 2 +- .../Layout/_mergeFiles/layout/page_empty.xml | 2 +- .../View/Layout/_mergeFiles/layout/print.xml | 2 +- .../_mergeFiles/layout/sales_guest_print.xml | 2 +- .../_mergeFiles/layout/sales_order_print.xml | 2 +- .../View/Layout/_mergeFiles/merged.xml | 2 +- .../View/LayoutArgumentObjectUpdater.php | 2 +- .../View/LayoutArgumentSimpleUpdater.php | 2 +- .../Framework/View/LayoutDirectivesTest.php | 14 +- .../Magento/Framework/View/LayoutTest.php | 2 +- .../View/LayoutTestWithExceptions.php | 2 +- .../Framework/View/Model/Layout/MergeTest.php | 2 +- .../_files/layout/fixture_handle_one.xml | 2 +- .../_files/layout/fixture_handle_two.xml | 2 +- .../View/Page/Config/Reader/HtmlTest.php | 2 +- .../Config/Reader/_files/_layout_update.xml | 2 +- .../Magento/Framework/View/Utility/Layout.php | 2 +- .../Framework/View/Utility/LayoutTest.php | 2 +- .../View/Utility/_files/layout/handle_one.xml | 2 +- .../Utility/_files/layout/handle_three.xml | 2 +- .../View/Utility/_files/layout/handle_two.xml | 2 +- .../_files/layout_merged/multiple_handles.xml | 2 +- .../_files/layout_merged/single_handle.xml | 2 +- .../_files/Fixture_Module/registration.php | 2 +- .../view/adminhtml/product/product.css | 2 +- .../Magento/ModuleC/view/adminhtml/styles.css | 2 +- .../_files/UiComponent/expected/config.xml | 17 + .../Magento_Catalog/ui_component/test.xml | 14 + .../Magento_Customer/ui_component/test.xml | 14 + .../_files/UiComponent/theme/registration.php | 11 + .../View/_files/UiComponent/theme/theme.xml | 9 + .../app/code/ViewTest_Module/registration.php | 2 +- .../templates/fixture_template_two.phtml | 2 +- .../ViewTest_Module/web/fixture_script_two.js | 2 +- .../Vendor/custom_theme/registration.php | 2 +- .../templates/fixture_template_two.phtml | 2 +- .../frontend/Vendor/custom_theme/theme.xml | 2 +- .../custom_theme/web/fixture_script_two.js | 2 +- .../Vendor/custom_theme/web/mage/script.js | 2 +- .../Vendor/custom_theme2/registration.php | 2 +- .../frontend/Vendor/custom_theme2/theme.xml | 2 +- .../templates/fixture_template.phtml | 2 +- .../ViewTest_Module/web/fixture_script.js | 2 +- .../web/i18n/ru_RU/fixture_script.js | 2 +- .../frontend/Vendor/default/registration.php | 2 +- .../default/templates/fixture_template.phtml | 2 +- .../design/frontend/Vendor/default/theme.xml | 2 +- .../Vendor/default/web/fixture_script.js | 2 +- .../default/web/i18n/ru_RU/fixture_script.js | 2 +- .../Vendor/standalone_theme/registration.php | 2 +- .../Vendor/standalone_theme/theme.xml | 2 +- .../_files/fallback/lib/web/mage/script.js | 2 +- .../View/_files/layout/cacheable.xml | 2 +- .../_files/layout/container_attributes.xml | 2 +- .../View/_files/layout/non_cacheable.xml | 2 +- .../action_for_anonymous_parent_block.xml | 2 +- .../layout_directives_test/arguments.xml | 2 +- .../arguments_complex_values.xml | 2 +- .../arguments_object_type.xml | 2 +- .../arguments_object_type_updaters.xml | 2 +- .../arguments_url_type.xml | 2 +- .../layout_directives_test/get_block.xml | 2 +- .../_files/layout_directives_test/group.xml | 2 +- .../layout_directives_test/ifconfig.xml | 2 +- .../_files/layout_directives_test/move.xml | 2 +- .../move_alias_broken.xml | 2 +- .../layout_directives_test/move_broken.xml | 2 +- .../layout_directives_test/move_new_alias.xml | 2 +- .../move_the_same_alias.xml | 2 +- .../_files/layout_directives_test/remove.xml | 2 +- .../layout_directives_test/remove_broken.xml | 2 +- .../remove_cancellation.xml | 21 + .../_files/layout_directives_test/render.xml | 2 +- .../sort_after_after.xml | 2 +- .../sort_after_previous.xml | 2 +- .../sort_before_after.xml | 2 +- .../sort_before_before.xml | 2 +- .../_files/layout_with_exceptions/layout.xml | 2 +- .../View/_files/static/theme/registration.php | 2 +- .../View/_files/static/theme/theme.xml | 2 +- .../theme/web/css/preminified-styles.min.css | 4 +- .../_files/static/theme/web/css/styles.css | 4 +- .../View/_files/static/theme/web/js/test.js | 2 +- .../Model/OrderItemRepositoryTest.php | 2 +- .../GiftMessage/Model/OrderRepositoryTest.php | 2 +- .../GiftMessage/_files/empty_order.php | 2 +- .../GiftMessage/_files/order_with_message.php | 2 +- .../quote_with_customer_and_message.php | 2 +- ...ote_with_customer_and_message_rollback.php | 2 +- .../_files/quote_with_item_message.php | 2 +- .../quote_with_item_message_rollback.php | 2 +- .../GiftMessage/_files/quote_with_message.php | 2 +- .../_files/quote_with_message_rollback.php | 2 +- .../GiftMessage/_files/virtual_order.php | 2 +- .../Model/Validator/FactoryTest.php | 2 +- .../GroupedImportExport/Model/GroupedTest.php | 2 +- .../Model/Import/Product/Type/GroupedTest.php | 2 +- .../Model/Product/Type/GroupedTest.php | 49 +- .../AssociatedProductsCollectionTest.php | 2 +- .../Pricing/Price/FinalPriceTest.php | 2 +- .../GroupedProduct/_files/product_grouped.php | 2 +- .../_files/product_grouped_rollback.php | 2 +- .../Block/Adminhtml/Export/Edit/FormTest.php | 8 +- .../Block/Adminhtml/Export/FilterTest.php | 2 +- .../Adminhtml/Import/Edit/BeforeTest.php | 2 +- .../Block/Adminhtml/Import/Edit/FormTest.php | 2 +- .../Controller/Adminhtml/ExportTest.php | 4 +- .../Adminhtml/Import/HttpFactoryMock.php | 2 +- .../Adminhtml/Import/ValidateTest.php | 2 +- .../Controller/Adminhtml/ImportTest.php | 2 +- .../Model/Export/AbstractStubEntity.php | 2 +- .../Model/Export/Entity/AbstractEavTest.php | 2 +- .../Model/Export/EntityAbstractTest.php | 2 +- .../Magento/ImportExport/Model/ExportTest.php | 2 +- .../Model/Import/Entity/EavAbstractTest.php | 2 +- .../Model/Import/EntityAbstractTest.php | 2 +- .../Magento/ImportExport/Model/ImportTest.php | 2 +- .../Model/ResourceModel/Import/DataTest.php | 2 +- .../Model/Source/Import/EntityTest.php | 2 +- .../ImportExport/_files/import_data.php | 2 +- .../Magento/ImportExport/_files/product.php | 2 +- .../Controller/Adminhtml/IndexerTest.php | 2 +- .../Indexer/Model/Config/ConverterTest.php | 2 +- .../Indexer/Model/Config/_files/indexer.xml | 2 +- .../Indexer/Model/Config/_files/result.php | 2 +- .../Activate/Permissions/Tab/WebapiTest.php | 2 +- .../Adminhtml/Integration/Edit/FormTest.php | 2 +- .../Integration/Edit/Tab/InfoTest.php | 2 +- .../Block/Adminhtml/Integration/EditTest.php | 2 +- .../Block/Adminhtml/Integration/GridTest.php | 2 +- .../Adminhtml/Integration/TokensTest.php | 2 +- .../Column/Renderer/Button/DeleteTest.php | 2 +- .../Grid/Column/Renderer/Button/EditTest.php | 2 +- .../Column/Renderer/Link/ActivateTest.php | 2 +- .../Controller/Adminhtml/IntegrationTest.php | 2 +- .../Model/AdminTokenServiceTest.php | 2 +- .../Model/AuthorizationServiceTest.php | 2 +- .../Model/Config/Consolidated/ReaderTest.php | 2 +- .../Consolidated/_files/integration.php | 2 +- .../Consolidated/_files/integrationA.xml | 2 +- .../Consolidated/_files/integrationB.xml | 2 +- .../Model/Config/Integration/ReaderTest.php | 2 +- .../Model/Config/Integration/_files/api.php | 2 +- .../Model/Config/Integration/_files/apiA.xml | 2 +- .../Model/Config/Integration/_files/apiB.xml | 2 +- .../Integration/Model/Config/ReaderTest.php | 2 +- .../Model/Config/_files/configA.xml | 2 +- .../Model/Config/_files/configB.xml | 2 +- .../Model/Config/_files/integration.php | 2 +- .../Model/CustomerTokenServiceTest.php | 2 +- .../Model/ResourceModel/IntegrationTest.php | 2 +- .../_files/integration_all_permissions.php | 2 +- .../integration_all_permissions_rollback.php | 2 +- .../MediaStorage/Model/File/StorageTest.php | 2 +- .../testsuite/Magento/MemoryUsageTest.php | 6 +- .../Block/Checkout/Address/SelectTest.php | 2 +- .../Block/Checkout/AddressesTest.php | 2 +- .../Block/Checkout/OverviewTest.php | 2 +- .../Multishipping/Controller/CheckoutTest.php | 2 +- .../Model/Checkout/Type/MultishippingTest.php | 2 +- .../Block/Adminhtml/Queue/Edit/FormTest.php | 2 +- .../Block/Adminhtml/SubscriberTest.php | 2 +- .../Adminhtml/NewsletterQueueTest.php | 2 +- .../Adminhtml/NewsletterTemplateTest.php | 2 +- .../Newsletter/Controller/ManageTest.php | 2 +- .../Newsletter/Controller/SubscriberTest.php | 2 +- .../Magento/Newsletter/Helper/DataTest.php | 2 +- .../Newsletter/Model/Plugin/PluginTest.php | 2 +- .../Magento/Newsletter/Model/QueueTest.php | 2 +- .../ResourceModel/Problem/CollectionTest.php | 2 +- .../Subscriber/CollectionTest.php | 2 +- .../Model/ResourceModel/SubscriberTest.php | 2 +- .../Newsletter/Model/SubscriberTest.php | 2 +- .../Magento/Newsletter/Model/TemplateTest.php | 2 +- .../Newsletter/_files/newsletter_sample.php | 2 +- .../Magento/Newsletter/_files/problems.php | 2 +- .../Magento/Newsletter/_files/queue.php | 2 +- .../Magento/Newsletter/_files/subscribers.php | 2 +- .../_files/subscribers_rollback.php | 2 +- .../Magento/Newsletter/_files/template.php | 2 +- .../OfflineShipping/_files/tablerates.php | 28 + .../_files/tablerates_rollback.php | 12 + .../PageCache/Block/JavascriptTest.php | 2 +- .../System/Config/Form/Field/ExportTest.php | 6 +- .../Magento/PageCache/Model/ConfigTest.php | 67 + .../PageCache/Model/Layout/MergeTest.php | 37 + .../Model/System/Config/Backend/TtlTest.php | 2 +- .../System/Config/Backend/VarnishTest.php | 2 +- .../Magento/PageCache/Model/_files/result.vcl | 12 + .../Magento/PageCache/Model/_files/test.vcl | 8 + .../Magento/Payment/Block/InfoTest.php | 2 +- .../Payment/Block/Transparent/IframeTest.php | 2 +- .../Magento/Payment/Helper/DataTest.php | 2 +- .../Payment/Model/Config/ReaderTest.php | 2 +- .../Magento/Payment/Model/ConfigTest.php | 2 +- .../Magento/Payment/Model/_files/payment.xml | 2 +- .../Magento/Payment/Model/_files/payment2.xml | 2 +- ...derStatusForPaymentMethodsObserverTest.php | 2 +- .../Magento/Payment/_files/order_status.php | 2 +- .../Paypal/Adminhtml/Paypal/ReportsTest.php | 2 +- .../Billing/Agreement/View/Tab/InfoTest.php | 2 +- .../Block/Billing/Agreement/ViewTest.php | 2 +- .../Magento/Paypal/Block/Bml/BannersTest.php | 2 +- .../Block/Express/Review/BillingTest.php | 2 +- .../Paypal/Block/Express/ReviewTest.php | 2 +- .../Payment/Form/Billing/AgreementTest.php | 2 +- .../Billing/Agreement/CancelTest.php | 2 +- .../Billing/Agreement/DeleteTest.php | 2 +- .../Adminhtml/Billing/Agreement/GridTest.php | 2 +- .../Adminhtml/Billing/Agreement/IndexTest.php | 2 +- .../Adminhtml/Billing/Agreement/ViewTest.php | 2 +- .../Adminhtml/Billing/AgreementTest.php | 2 +- .../Adminhtml/Paypal/Reports/DetailsTest.php | 2 +- .../Adminhtml/Paypal/Reports/FetchTest.php | 2 +- .../Adminhtml/Paypal/Reports/IndexTest.php | 2 +- .../Controller/Billing/AgreementTest.php | 2 +- .../Magento/Paypal/Controller/ExpressTest.php | 2 +- .../Paypal/Controller/HostedproTest.php | 2 +- .../Magento/Paypal/Controller/PayflowTest.php | 2 +- .../Paypal/Controller/PayflowadvancedTest.php | 2 +- .../Config/Structure/Reader/ConverterStub.php | 2 +- .../Config/Structure/Reader/ReaderStub.php | 2 +- .../Config/Structure/Reader/ReaderTest.php | 2 +- .../Structure/Reader/_files/actual/config.xml | 2 +- .../Reader/_files/expected/config.xml | 5 +- .../Paypal/Model/Express/CheckoutTest.php | 193 +- .../Paypal/Model/Hostedpro/RequestTest.php | 2 +- .../Magento/Paypal/Model/HostedproTest.php | 2 +- .../Magento/Paypal/Model/IpnTest.php | 2 +- .../Magento/Paypal/Model/PayflowproTest.php | 2 +- .../Method/Billing/AbstractAgreementTest.php | 2 +- .../Paypal/Model/Report/SettlementTest.php | 2 +- .../Billing/Agreement/CollectionTest.php | 2 +- .../Magento/Paypal/Model/VoidTest.php | 2 +- .../Magento/Paypal/_files/address_data.php | 2 +- .../Paypal/_files/billing_agreement.php | 2 +- .../testsuite/Magento/Paypal/_files/ipn.php | 2 +- .../Magento/Paypal/_files/order_express.php | 2 +- .../Magento/Paypal/_files/order_hostedpro.php | 2 +- .../Paypal/_files/order_payflowpro.php | 2 +- .../Magento/Paypal/_files/quote_express.php | 95 + .../_files/quote_express_with_customer.php | 77 + .../Magento/Paypal/_files/quote_payment.php | 2 +- .../Paypal/_files/quote_payment_express.php | 93 +- .../quote_payment_express_with_customer.php | 76 +- .../Paypal/_files/quote_payment_payflow.php | 2 +- .../Block/Header/AdditionalTest.php | 2 +- .../Magento/Persistent/Model/ObserverTest.php | 2 +- .../Model/Persistent/ConfigTest.php | 2 +- .../Model/Persistent/_files/expectedArray.php | 2 +- .../Persistent/_files/expectedBlocksArray.php | 2 +- .../Model/Persistent/_files/persistent.xml | 2 +- .../Magento/Persistent/Model/SessionTest.php | 2 +- .../Observer/EmulateCustomerObserverTest.php | 2 +- .../Observer/EmulateQuoteObserverTest.php | 2 +- ...nchronizePersistentOnLoginObserverTest.php | 2 +- ...chronizePersistentOnLogoutObserverTest.php | 2 +- .../Magento/Persistent/_files/persistent.php | 2 +- .../Magento/ProductAlert/Model/EmailTest.php | 2 +- .../ProductAlert/Model/ObserverTest.php | 2 +- .../ProductAlert/_files/product_alert.php | 2 +- .../Magento/Quote/Model/Quote/AddressTest.php | 26 +- .../Quote/Model/Quote/Item/RepositoryTest.php | 2 +- .../Quote/Model/QuoteManagementTest.php | 2 +- .../Quote/Model/QuoteRepositoryTest.php | 141 +- .../Magento/Quote/Model/QuoteTest.php | 61 +- .../Quote/Model/ResourceModel/QuoteTest.php | 45 + .../Model/ShippingMethodManagementTest.php | 95 + .../Address/CollectTotalsObserverTest.php | 2 +- .../Magento/Quote/_files/empty_quote.php | 2 +- .../Quote/_files/empty_quote_rollback.php | 2 +- .../Quote/_files/is_not_salable_product.php | 40 + .../Quote/_files/is_salable_product.php | 40 + .../Quote/etc/extension_attributes.xml | 2 +- .../Block/Adminhtml/Filter/FormTest.php | 2 +- .../Reports/Block/Adminhtml/GridTest.php | 2 +- .../Adminhtml/Sales/Bestsellers/GridTest.php | 2 +- .../Adminhtml/Sales/Coupons/GridTest.php | 2 +- .../Adminhtml/Sales/Invoiced/GridTest.php | 2 +- .../Adminhtml/Sales/Refunded/GridTest.php | 2 +- .../Block/Adminhtml/Sales/Sales/GridTest.php | 2 +- .../Adminhtml/Sales/Shipping/GridTest.php | 2 +- .../Block/Adminhtml/Sales/Tax/GridTest.php | 2 +- .../Adminhtml/Shopcart/Abandoned/GridTest.php | 2 +- .../Adminhtml/Shopcart/GridTestAbstract.php | 2 +- .../Adminhtml/Shopcart/Product/GridTest.php | 2 +- .../Magento/Reports/Block/WidgetTest.php | 2 +- .../Adminhtml/Report/Product/ViewedTest.php | 2 +- .../Product/Lowstock/CollectionTest.php | 2 +- .../Report/Product/Viewed/CollectionTest.php | 2 +- .../Review/Product/CollectionTest.php | 2 +- .../Magento/Reports/_files/orders.php | 2 +- .../Reports/_files/viewed_products.php | 2 +- .../Review/Block/Adminhtml/Edit/FormTest.php | 2 +- .../Block/Adminhtml/Edit/Tab/FormTest.php | 2 +- .../Review/Block/Adminhtml/MainTest.php | 2 +- .../Magento/Review/Controller/ProductTest.php | 2 +- .../ResourceModel/Rating/CollectionTest.php | 2 +- .../Review/Model/ResourceModel/RatingTest.php | 2 +- .../Review/Product/CollectionTest.php | 2 +- .../Model/ResourceModel/Review/ReviewTest.php | 2 +- .../Magento/Review/_files/customer_review.php | 2 +- .../_files/customer_review_with_rating.php | 2 +- .../Review/_files/different_reviews.php | 2 +- .../Magento/Review/_files/review_xss.php | 2 +- .../Magento/Review/_files/reviews.php | 2 +- .../Rule/Model/Condition/AbstractTest.php | 2 +- ...editmemoCommentRepositoryInterfaceTest.php | 2 +- .../CreditmemoItemRepositoryInterfaceTest.php | 2 +- .../InvoiceCommentRepositoryInterfaceTest.php | 2 +- .../InvoiceItemRepositoryInterfaceTest.php | 2 +- ...erStatusHistoryRepositoryInterfaceTest.php | 2 +- ...ShipmentCommentRepositoryInterfaceTest.php | 2 +- .../ShipmentItemRepositoryInterfaceTest.php | 2 +- .../ShipmentTrackRepositoryInterfaceTest.php | 2 +- .../Block/Adminhtml/Items/AbstractTest.php | 2 +- .../Order/Create/Form/AbstractTest.php | 2 +- .../Order/Create/Form/AccountTest.php | 2 +- .../Order/Create/Form/AddressTest.php | 2 +- .../Block/Adminhtml/Order/Create/FormTest.php | 2 +- .../Order/Create/Giftmessage/FormTest.php | 2 +- .../Adminhtml/Order/Create/HeaderTest.php | 2 +- .../Block/Adminhtml/Order/View/InfoTest.php | 2 +- .../Report/Filter/Form/CouponTest.php | 2 +- .../Sales/Block/Order/CommentsTest.php | 2 +- .../Block/Order/Creditmemo/ItemsTest.php | 2 +- .../Sales/Block/Order/Invoice/ItemsTest.php | 2 +- .../Magento/Sales/Block/Order/ItemsTest.php | 135 + .../Block/Order/PrintOrder/CreditmemoTest.php | 2 +- .../Block/Order/PrintOrder/InvoiceTest.php | 2 +- .../Magento/Sales/Block/Order/TotalsTest.php | 2 +- .../Adminhtml/Order/AddCommentTest.php | 2 +- .../Adminhtml/Order/AddressSaveTest.php | 2 +- .../Adminhtml/Order/AddressTest.php | 2 +- .../Adminhtml/Order/AuthorizationMock.php | 2 +- .../Controller/Adminhtml/Order/CancelTest.php | 2 +- .../Controller/Adminhtml/Order/CreateTest.php | 2 +- .../Adminhtml/Order/CreditmemoTest.php | 2 +- .../Controller/Adminhtml/Order/EmailTest.php | 2 +- .../Controller/Adminhtml/Order/HoldTest.php | 2 +- .../Adminhtml/Order/ReviewPaymentTest.php | 2 +- .../Adminhtml/Order/Stub/OrderCreateStub.php | 2 +- .../Controller/Adminhtml/Order/UnholdTest.php | 2 +- .../Controller/Adminhtml/Order/ViewTest.php | 2 +- .../Adminhtml/Transactions/FetchTest.php | 2 +- .../Model/AbstractCollectorPositionsTest.php | 2 +- .../Sales/Model/AdminOrder/CreateTest.php | 87 +- .../Magento/Sales/Model/Convert/OrderTest.php | 2 +- .../Model/CronJob/CleanExpiredOrdersTest.php | 2 +- .../Model/Order/Address/RendererTest.php | 103 + .../Model/Order/AddressRepositoryTest.php | 2 +- .../Magento/Sales/Model/Order/AddressTest.php | 2 +- .../Model/Order/CreditmemoFactoryTest.php | 100 + .../Email/Sender/CreditmemoSenderTest.php | 2 +- .../Order/Email/Sender/InvoiceSenderTest.php | 2 +- .../Order/Email/Sender/OrderSenderTest.php | 2 +- .../Order/Email/Sender/ShipmentSenderTest.php | 2 +- .../Magento/Sales/Model/Order/InvoiceTest.php | 2 +- .../Magento/Sales/Model/Order/ItemTest.php | 55 + .../Model/Order/Payment/RepositoryTest.php | 2 +- .../Model/Order/Payment/TransactionTest.php | 2 +- .../Sales/Model/Order/ShipmentTest.php | 7 +- .../Model/ResourceModel/Order/StatusTest.php | 2 +- .../Sales/Model/ResourceModel/OrderTest.php | 2 +- .../Report/Bestsellers/CollectionTest.php | 2 +- .../Invoiced/Collection/InvoicedTest.php | 2 +- .../Report/Invoiced/Collection/OrderTest.php | 2 +- .../Report/Refunded/Collection/OrderTest.php | 2 +- .../Refunded/Collection/RefundedTest.php | 2 +- .../Report/Shipping/Collection/OrderTest.php | 2 +- .../Shipping/Collection/ShipmentTest.php | 2 +- .../ResourceModel/Sale/CollectionTest.php | 2 +- .../Observer/Backend/CustomerQuoteTest.php | 2 +- .../Magento/Sales/_files/address.php | 2 +- .../Magento/Sales/_files/address_data.php | 2 +- .../Magento/Sales/_files/address_list.php | 2 +- .../Sales/_files/address_list_rollback.php | 2 +- .../Sales/_files/assign_status_to_state.php | 2 +- .../Magento/Sales/_files/creditmemo.php | 2 +- .../_files/creditmemo_comments_for_search.php | 2 +- .../Sales/_files/creditmemo_for_get.php | 2 +- .../_files/creditmemo_for_get_rollback.php | 2 +- .../_files/creditmemo_items_for_search.php | 2 +- .../Magento/Sales/_files/creditmemo_list.php | 2 +- .../Sales/_files/creditmemo_list_rollback.php | 2 +- .../Sales/_files/creditmemo_rollback.php | 2 +- .../Sales/_files/creditmemo_with_list.php | 2 +- .../_files/creditmemo_with_list_rollback.php | 2 +- .../Magento/Sales/_files/default_rollback.php | 2 +- .../Magento/Sales/_files/invoice.php | 2 +- .../_files/invoice_comments_for_search.php | 2 +- .../_files/invoice_fixture_store_order.php | 2 +- .../Sales/_files/invoice_items_for_search.php | 2 +- .../Magento/Sales/_files/invoice_list.php | 2 +- .../Sales/_files/invoice_list_rollback.php | 2 +- .../Sales/_files/invoice_payflowpro.php | 2 +- .../Magento/Sales/_files/invoice_rollback.php | 2 +- .../testsuite/Magento/Sales/_files/order.php | 2 +- .../Sales/_files/order_alphanumeric_id.php | 2 +- .../Sales/_files/order_fixture_store.php | 75 +- .../_files/order_fixture_store_rollback.php | 17 + .../Magento/Sales/_files/order_info.php | 2 +- .../Magento/Sales/_files/order_item_list.php | 2 +- .../Magento/Sales/_files/order_list.php | 2 +- .../Sales/_files/order_list_rollback.php | 2 +- .../Magento/Sales/_files/order_new.php | 2 +- .../Sales/_files/order_new_rollback.php | 2 +- .../_files/order_paid_with_payflowpro.php | 2 +- .../Sales/_files/order_payment_list.php | 2 +- .../Sales/_files/order_pending_payment.php | 2 +- .../Magento/Sales/_files/order_rollback.php | 2 +- .../Magento/Sales/_files/order_shipping.php | 2 +- ..._shipping_address_different_to_billing.php | 2 +- ...order_shipping_address_same_as_billing.php | 2 +- .../order_status_history_for_search.php | 2 +- .../Sales/_files/order_with_customer.php | 2 +- .../order_with_dummy_item_and_invoiced.php | 73 + .../order_with_shipping_and_invoice.php | 2 +- ...der_with_shipping_and_invoice_rollback.php | 6 + .../testsuite/Magento/Sales/_files/quote.php | 2 +- .../Magento/Sales/_files/quote_rollback.php | 2 +- .../Sales/_files/quote_with_bundle.php | 2 +- .../_files/quote_with_bundle_rollback.php | 2 +- .../Sales/_files/quote_with_customer.php | 2 +- .../_files/quote_with_customer_rollback.php | 2 +- .../Sales/_files/report_bestsellers.php | 2 +- .../Magento/Sales/_files/report_invoiced.php | 2 +- .../Magento/Sales/_files/report_refunded.php | 2 +- .../Magento/Sales/_files/report_shipping.php | 2 +- .../Magento/Sales/_files/shipment.php | 2 +- .../_files/shipment_comments_for_search.php | 2 +- .../_files/shipment_items_for_search.php | 2 +- .../Magento/Sales/_files/shipment_list.php | 2 +- .../Sales/_files/shipment_list_rollback.php | 2 +- .../Sales/_files/shipment_rollback.php | 2 +- .../_files/shipment_tracks_for_search.php | 2 +- .../Magento/Sales/_files/transactions.php | 2 +- .../Sales/_files/transactions_detailed.php | 2 +- .../Sales/_files/transactions_list.php | 2 +- .../_files/transactions_list_rollback.php | 2 +- .../two_orders_for_one_of_two_customers.php | 2 +- .../two_orders_for_two_diff_customers.php | 2 +- .../Promo/Quote/Edit/Tab/LabelsTest.php | 2 +- .../Quote/Address/Total/ShippingTest.php | 106 + .../Report/Rule/CreatedatTest.php | 2 +- .../ResourceModel/Rule/CollectionTest.php | 35 +- .../Model/ResourceModel/RuleTest.php | 27 + .../_files/cart_rule_40_percent_off.php | 2 +- .../_files/cart_rule_50_percent_off.php | 2 +- .../_files/cart_rule_free_shipping.php | 2 +- .../cart_rule_free_shipping_rollback.php | 2 +- .../Magento/SalesRule/_files/coupons.php | 4 +- .../SalesRule/_files/coupons_advanced.php | 2 +- .../SalesRule/_files/order_with_coupon.php | 2 +- .../SalesRule/_files/report_coupons.php | 2 +- .../_files/rule_custom_product_attribute.php | 76 + .../rule_free_shipping_by_product_weight.php | 35 + .../SalesRule/_files/rule_specific_date.php | 2 +- .../Magento/SalesRule/_files/rules.php | 2 +- .../SalesRule/_files/rules_advanced.php | 2 +- .../SalesRule/_files/rules_autogeneration.php | 2 +- .../_files/rules_autogeneration_rollback.php | 2 +- .../SalesRule/_files/rules_categories.php | 2 +- .../_files/rules_categories_rollback.php | 2 +- .../SalesRule/_files/rules_category.php | 2 +- .../_files/rules_category_rollback.php | 2 +- .../_files/rules_group_all_categories.php | 2 +- ...es_group_all_categories_price_attr_set.php | 2 +- .../_files/rules_group_any_categories.php | 2 +- ...les_group_any_categories_price_address.php | 2 +- ...es_group_any_categories_price_attr_set.php | 2 +- ...roup_any_categories_price_attr_set_any.php | 2 +- ...roup_categories_price_sku_attr_set_any.php | 2 +- .../rules_group_not_categories_sku_attr.php | 2 +- .../SalesRule/_files/rules_rollback.php | 2 +- .../SampleData/Model/DependencyTest.php | 73 + .../_files/Modules/FirstModule/composer.json | 9 + .../_files/Modules/SecondModule/composer.json | 9 + .../_files/Modules/ThirdModule/composer.json | 6 + .../System/Config/Source/EngineTest.php | 2 +- .../Model/ResourceModel/SynonymGroupTest.php | 2 +- .../Search/Model/SearchEngine/ConfigTest.php | 2 +- .../Search/Model/SynonymAnalyzerTest.php | 2 +- .../Model/SynonymGroupRepositoryTest.php | 2 +- .../Search/Model/SynonymReaderTest.php | 2 +- .../Magento/Search/_files/search_engine.xml | 2 +- .../Magento/Search/_files/synonym_reader.php | 2 +- .../Search/_files/synonym_reader_rollback.php | 2 +- .../Adminhtml/Session/LogoutAllTest.php | 2 +- .../Model/AdminSessionsManagerTest.php | 2 +- .../Security/Model/Plugin/AuthSessionTest.php | 2 +- .../AdminSessionInfo/CollectionTest.php | 2 +- .../ResourceModel/AdminSessionInfoTest.php | 2 +- .../CollectionTest.php | 2 +- .../PasswordResetRequestEventTest.php | 2 +- .../Security/Model/SecurityManagerTest.php | 2 +- .../Magento/Security/_files/adminsession.php | 2 +- .../_files/password_reset_request_events.php | 2 +- .../Magento/SendFriend/Block/SendTest.php | 2 +- .../DependenciesShowFrameworkCommandTest.php | 2 +- ...ndenciesShowModulesCircularCommandTest.php | 2 +- .../DependenciesShowModulesCommandTest.php | 2 +- .../Command/I18nCollectPhrasesCommandTest.php | 3 +- .../Console/Command/I18nPackCommandTest.php | 2 +- .../Command/_files/phrases/TestPhrases.php | 2 +- .../root/app/code/Magento/A/Model/Foo.php | 2 +- .../root/app/code/Magento/A/composer.json | 2 +- .../root/app/code/Magento/A/etc/module.xml | 2 +- .../root/app/code/Magento/A/registration.php | 2 +- .../root/app/code/Magento/B/Model/Foo.php | 2 +- .../root/app/code/Magento/B/composer.json | 2 +- .../root/app/code/Magento/B/etc/module.xml | 2 +- .../root/app/code/Magento/B/registration.php | 2 +- .../root/app/code/Magento/C/registration.php | 2 +- .../root/app/code/Magento/D/registration.php | 2 +- .../Magento/Setup/Controller/UrlCheckTest.php | 98 + .../Setup/Fixtures/FixtureModelTest.php | 118 +- .../FixturesAsserts/BundleProductsAssert.php | 78 + .../ConfigurableProductsAssert.php | 90 + .../FixturesAsserts/ProductAssert.php | 80 + .../FixturesAsserts/SimpleProductsAssert.php | 56 + .../Setup/Fixtures/_files/attributeSets.xml | 132 + .../Magento/Setup/Fixtures/_files/small.xml | 47 +- .../Model/ConfigOptionsListCollectorTest.php | 2 +- .../Model/Cron/MultipleStreamOutputTest.php | 2 +- .../FixtureGenerator/ProductGeneratorTest.php | 108 + .../Setup/Model/ObjectManagerProviderTest.php | 2 +- .../Magento/Setup/Module/DataSetupTest.php | 2 +- .../Setup/Module/Dependency/CircularTest.php | 2 +- .../Dependency/Parser/Composer/JsonTest.php | 2 +- .../Dependency/Parser/Config/XmlTest.php | 2 +- .../Module/Dependency/Report/CircularTest.php | 2 +- .../Dependency/Report/DependencyTest.php | 2 +- .../Dependency/Report/FrameworkTest.php | 2 +- .../Magento/FirstModule/Helper/Helper.php | 2 +- .../code/Magento/FirstModule/Model/Model.php | 2 +- .../FirstModule/Model/WithoutDependencies.php | 2 +- .../code/Magento/FirstModule/etc/module.xml | 2 +- .../FirstModule/view/frontend/template.phtml | 2 +- .../Module/Dependency/_files/module1.xml | 2 +- .../Module/Dependency/_files/module2.xml | 2 +- .../Module/I18n/Dictionary/GeneratorTest.php | 2 +- .../Magento/FirstModule/Helper/Helper.php | 2 +- .../code/Magento/FirstModule/Model/Model.php | 2 +- .../FirstModule/view/frontend/default.xml | 2 +- .../Magento/FirstModule/view/frontend/file.js | 2 +- .../FirstModule/view/frontend/template.phtml | 2 +- .../code/Magento/SecondModule/Model/Model.php | 2 +- .../adminhtml/default/backend/default.xml | 2 +- .../adminhtml/default/backend/template.phtml | 2 +- .../_files/source/lib/web/mage/file.js | 2 +- .../_files/source/lib/web/varien/file.js | 2 +- .../_files/source/not_magento_dir/Model.php | 2 +- .../_files/source/not_magento_dir/file.js | 2 +- .../source/not_magento_dir/template.phtml | 2 +- .../Setup/Module/I18n/Pack/GeneratorTest.php | 2 +- .../Module/I18n/Parser/Adapter/JsTest.php | 2 +- .../Php/Tokenizer/PhraseCollectorTest.php | 2 +- .../Translate/MethodCollectorTest.php | 2 +- .../Module/I18n/Parser/Adapter/XmlTest.php | 2 +- .../Adapter/_files/xmlPhrasesForTest.xml | 2 +- .../Magento/Shipping/Block/ItemsTest.php | 2 +- .../Magento/Shipping/Helper/DataTest.php | 2 +- .../Magento/Shipping/Model/ShippingTest.php | 86 + .../Magento/Sitemap/Helper/DataTest.php | 2 +- .../ResourceModel/Catalog/ProductTest.php | 2 +- .../Sitemap/_files/sitemap_products.php | 2 +- .../_files/sitemap_products_rollback.php | 2 +- .../App/Request/PathInfoProcessorTest.php | 2 +- .../Controller/Store/SwitchActionTest.php | 4 +- .../Magento/Store/Model/App/EmulationTest.php | 2 +- .../Magento/Store/Model/DataSource.php | 2 +- .../Magento/Store/Model/GroupTest.php | 2 +- .../ResourceModel/Store/CollectionTest.php | 2 +- .../Store/Model/ResourceModel/StoreTest.php | 2 +- .../Store/Model/ResourceModel/WebsiteTest.php | 2 +- .../Store/Model/StoreCookieManagerTest.php | 2 +- .../Magento/Store/Model/StoreManagerTest.php | 48 + .../Magento/Store/Model/StoreResolverTest.php | 37 + .../Magento/Store/Model/StoreTest.php | 2 +- .../Magento/Store/Model/WebsiteTest.php | 2 +- .../Store/_files/core_fixturestore.php | 4 +- .../_files/core_fixturestore_rollback.php | 2 +- .../_files/core_second_third_fixturestore.php | 2 +- ...fixture_store_with_catalogsearch_index.php | 2 +- ...tore_with_catalogsearch_index_rollback.php | 2 +- .../Store/_files/scope.config.fixture.php | 11 + .../Magento/Store/_files/second_store.php | 14 +- .../Store/_files/second_store_rollback.php | 2 +- .../second_website_with_second_currency.php | 45 + ..._website_with_second_currency_rollback.php | 31 + .../_files/second_website_with_two_stores.php | 63 + ...econd_website_with_two_stores_rollback.php | 29 + .../testsuite/Magento/Store/_files/store.php | 7 +- .../Magento/Store/_files/website.php | 6 +- .../Magento/Store/_files/website_rollback.php | 2 +- .../Swatches/Model/AttributeCreateTest.php | 89 + .../Tax/Block/Adminhtml/Rate/TitleTest.php | 2 +- .../Tax/Controller/Adminhtml/RateTest.php | 2 +- .../Tax/Controller/Adminhtml/TaxTest.php | 2 +- .../testsuite/Magento/Tax/Helper/DataTest.php | 2 +- .../Model/Calculation/RateRepositoryTest.php | 2 +- .../Magento/Tax/Model/CalculationTest.php | 2 +- .../testsuite/Magento/Tax/Model/ClassTest.php | 2 +- .../Magento/Tax/Model/ConfigTest.php | 2 +- .../Magento/Tax/Model/Rate/SourceTest.php | 2 +- .../Calculation/Rule/CollectionTest.php | 2 +- .../Model/ResourceModel/CalculationTest.php | 2 +- .../ResourceModel/Report/CollectionTest.php | 2 +- .../Tax/Model/Sales/Total/Quote/SetupUtil.php | 2 +- .../Model/Sales/Total/Quote/SubtotalTest.php | 3 +- .../Tax/Model/Sales/Total/Quote/TaxTest.php | 2 +- .../Magento/Tax/Model/TaxCalculationTest.php | 2 +- .../Tax/Model/TaxClass/ManagementTest.php | 2 +- .../Tax/Model/TaxClass/RepositoryTest.php | 2 +- .../Model/TaxClass/Source/CustomerTest.php | 2 +- .../Tax/Model/TaxClass/Source/ProductTest.php | 2 +- .../Tax/Model/TaxClass/Type/CustomerTest.php | 2 +- .../Tax/Model/TaxRateCollectionTest.php | 2 +- .../Tax/Model/TaxRateManagementTest.php | 2 +- .../Tax/Model/TaxRuleCollectionTest.php | 2 +- .../Tax/Model/TaxRuleFixtureFactory.php | 2 +- .../Tax/Model/TaxRuleRepositoryTest.php | 2 +- .../Magento/Tax/Pricing/AdjustmentTest.php | 2 +- .../Magento/Tax/_files/order_with_tax.php | 2 +- .../Magento/Tax/_files/report_tax.php | 2 +- ...excluding_tax_apply_tax_after_discount.php | 2 +- ..._apply_tax_after_discount_discount_tax.php | 2 +- ...xcluding_tax_apply_tax_before_discount.php | 2 +- .../excluding_tax_multi_item_row.php | 2 +- .../excluding_tax_multi_item_total.php | 2 +- .../excluding_tax_multi_item_unit.php | 2 +- .../_files/scenarios/excluding_tax_row.php | 2 +- .../_files/scenarios/excluding_tax_total.php | 2 +- .../_files/scenarios/excluding_tax_unit.php | 2 +- ...luding_tax_cross_border_trade_disabled.php | 2 +- ...cluding_tax_cross_border_trade_enabled.php | 2 +- .../_files/scenarios/including_tax_row.php | 2 +- .../_files/scenarios/including_tax_total.php | 2 +- .../_files/scenarios/including_tax_unit.php | 2 +- ...i_tax_rule_total_calculate_subtotal_no.php | 2 +- ..._tax_rule_total_calculate_subtotal_yes.php | 2 +- ...ule_two_row_calculate_subtotal_yes_row.php | 2 +- ...e_two_row_calculate_subtotal_yes_total.php | 2 +- ...ti_tax_rule_unit_calculate_subtotal_no.php | 2 +- ...i_tax_rule_unit_calculate_subtotal_yes.php | 2 +- .../tax_calculation_data_aggregated.php | 2 +- .../Magento/Tax/_files/tax_classes.php | 2 +- .../Block/Adminhtml/Rate/ImportExportTest.php | 2 +- .../Adminhtml/Rate/ExportPostTest.php | 2 +- .../Adminhtml/Rate/ImportExportTest.php | 2 +- .../Adminhtml/Rate/ImportPostTest.php | 2 +- .../Model/Rate/CsvImportHandlerTest.php | 2 +- .../Magento/Test/Integrity/DatabaseTest.php | 2 +- .../Magento/Test/Integrity/LayoutTest.php | 2 +- .../Integrity/Magento/Payment/MethodsTest.php | 2 +- .../Magento/Widget/SkinFilesTest.php | 2 +- .../Magento/Widget/TemplateFilesTest.php | 2 +- .../Modular/AbstractMergedConfigTest.php | 2 +- .../Integrity/Modular/AclConfigFilesTest.php | 2 +- .../Modular/BlockInstantiationTest.php | 2 +- .../Test/Integrity/Modular/CacheFilesTest.php | 2 +- .../Modular/CarrierConfigFilesTest.php | 2 +- .../Modular/CrontabConfigFilesTest.php | 2 +- .../Integrity/Modular/DiConfigFilesTest.php | 2 +- .../Modular/EavAttributesConfigFilesTest.php | 2 +- .../Modular/EventConfigFilesTest.php | 2 +- .../Modular/ExportConfigFilesTest.php | 2 +- .../Modular/FieldsetConfigFilesTest.php | 2 +- .../Modular/ImportConfigFilesTest.php | 2 +- .../Modular/IndexerConfigFilesTest.php | 2 +- .../Integrity/Modular/LayoutFilesTest.php | 2 +- .../Catalog/AttributeConfigFilesTest.php | 2 +- .../Customer/AddressFormatsFilesTest.php | 2 +- .../Email/EmailTemplateConfigFilesTest.php | 2 +- .../Magento/Sales/PdfConfigFilesTest.php | 2 +- .../Integrity/Modular/MenuConfigFilesTest.php | 2 +- .../Modular/MviewConfigFilesTest.php | 2 +- .../Modular/PaymentConfigFilesTest.php | 2 +- .../Modular/ProductOptionsConfigFilesTest.php | 2 +- .../Modular/ProductTypesConfigFilesTest.php | 2 +- .../Modular/ResourcesConfigFilesTest.php | 2 +- .../Modular/RouteConfigFilesTest.php | 2 +- .../Modular/SalesConfigFilesTest.php | 2 +- .../Modular/SystemConfigFilesTest.php | 2 +- .../Integrity/Modular/TemplateFilesTest.php | 2 +- .../Modular/ThemeConfigFilesTest.php | 2 +- .../Integrity/Modular/ViewConfigFilesTest.php | 2 +- .../Modular/WidgetConfigFilesTest.php | 2 +- .../Modular/_files/skip_blocks_ce.php | 2 +- .../_files/skip_template_blocks_ce.php | 2 +- .../Test/Integrity/StaticFilesTest.php | 2 +- .../Integrity/Theme/TemplateFilesTest.php | 2 +- .../Test/Integrity/Theme/XmlFilesTest.php | 2 +- .../Test/Integrity/ViewFileReferenceTest.php | 2 +- .../Controller/Adminhtml/Noroute.php | 2 +- .../ModuleInstallationTest.php | 2 +- .../Design/Theme/Edit/Tab/GeneralTest.php | 2 +- .../Theme/Block/Html/BreadcrumbsTest.php | 2 +- .../Magento/Theme/Block/Html/FooterTest.php | 2 +- .../System/Design/ThemeControllerTest.php | 2 +- .../System/Design/_files/simple-js-file.js | 2 +- .../Theme/Model/Config/ValidatorTest.php | 2 +- .../Model/Design/Backend/ExceptionsTest.php | 40 +- .../Magento/Theme/Model/DesignTest.php | 11 +- .../Theme/Model/Layout/Config/ReaderTest.php | 2 +- .../Magento/Theme/Model/Layout/ConfigTest.php | 2 +- .../Model/Layout/_files/page_layouts.xml | 2 +- .../Model/Layout/_files/page_layouts2.xml | 2 +- .../ResourceModel/Theme/CollectionTest.php | 2 +- .../Theme/Model/Theme/CollectionTest.php | 2 +- .../Theme/Model/Theme/Domain/VirtualTest.php | 5 +- .../Magento/Theme/Model/Theme/FileTest.php | 2 +- .../Theme/Model/Theme/RegistrationTest.php | 2 +- .../Theme/Model/Theme/Source/ThemeTest.php | 2 +- .../_files/design/frontend/a_d/theme.xml | 2 +- .../_files/design/frontend/b_e/theme.xml | 2 +- .../design/frontend/magento_default/theme.xml | 2 +- .../design/frontend/magento_g/theme.xml | 2 +- .../Theme/Model/Theme/ThemeProviderTest.php | 70 + .../Magento/Theme/Model/ThemeTest.php | 2 +- .../Magento/Theme/Model/View/DesignTest.php | 2 +- .../adminhtml/Vendor/test/registration.php | 2 +- .../design/adminhtml/Vendor/test/theme.xml | 2 +- .../Vendor/theme_one/registration.php | 2 +- .../area_two/Vendor/theme_one/theme.xml | 2 +- .../Vendor/theme_one/registration.php | 2 +- .../design_area/Vendor/theme_one/theme.xml | 2 +- .../frontend/Magento/default/registration.php | 2 +- .../design/frontend/Magento/default/theme.xml | 2 +- .../Magento/default_iphone/registration.php | 2 +- .../frontend/Magento/default_iphone/theme.xml | 2 +- .../Test/cache_test_theme/registration.php | 2 +- .../frontend/Test/cache_test_theme/theme.xml | 2 +- .../Magento_Catalog/catalog_category_view.xml | 2 +- .../catalog_category_view_type_default.xml | 2 +- .../Magento_Catalog/catalog_product_view.xml | 2 +- .../catalog_product_view_type_simple.xml | 2 +- .../templates/theme_template.phtml | 2 +- .../Magento_Cms/layout_test_handle_extra.xml | 2 +- .../Magento_Core/layout_test_handle_main.xml | 2 +- .../layout_test_handle_sample.xml | 2 +- .../design/frontend/Test/default/etc/view.xml | 2 +- .../frontend/Test/default/registration.php | 2 +- .../design/frontend/Test/default/theme.xml | 2 +- .../frontend/Test/default/web/css/styles.css | 2 +- .../frontend/Test/default/web/js/tabs.js | 2 +- .../Test/default/web/result_source.css | 4 +- .../Test/default/web/result_source_dev.css | 2 +- .../frontend/Test/default/web/source.less | 2 +- .../Test/publication/registration.php | 2 +- .../frontend/Test/publication/theme.xml | 2 +- .../frontend/Test/test_theme/registration.php | 2 +- .../design/frontend/Test/test_theme/theme.xml | 2 +- .../Fixture_Module/web/fixture_script.js | 2 +- .../Vendor/custom_theme/registration.php | 2 +- .../frontend/Vendor/custom_theme/theme.xml | 2 +- .../Vendor/default/access_violation.php | 2 +- .../frontend/Vendor/default/registration.php | 2 +- .../design/frontend/Vendor/default/theme.xml | 2 +- .../Vendor/default/web/css/base64.css | 2 +- .../Vendor/default/web/css/deep/recursive.css | 2 +- .../Vendor/default/web/css/exception.css | 2 +- .../frontend/Vendor/default/web/css/file.css | 2 +- .../frontend/Vendor/default/web/recursive.css | 2 +- .../frontend/Vendor/default/web/scripts.js | 2 +- .../design/frontend/access_violation.php | 2 +- .../Magento/Theme/_files/design_change.php | 2 +- .../Theme/_files/design_change_rollback.php | 2 +- .../Theme/_files/design_change_timezone.php | 2 +- .../design_change_timezone_rollback.php | 2 +- .../Translation/Controller/AjaxTest.php | 2 +- .../Translation/Model/InlineParserTest.php | 2 +- .../Magento/Translation/Model/StringTest.php | 2 +- .../local_config/custom/local.xml | 2 +- .../custom/prohibited.filename.xml | 2 +- .../local_config/local_config/local.xml | 2 +- .../_files/local_config/local_config/z.xml | 2 +- .../_files/local_config/no_local_config/a.xml | 2 +- .../_files/local_config/no_local_config/b.xml | 2 +- .../no_local_config/custom/local.xml | 2 +- .../Model/_files/locale/en_AU/config.xml | 2 +- .../Translation/_files/db_translate.php | 2 +- .../_files/db_translate_admin_store.php | 2 +- .../Magento/Ui/Api/BookmarkRepositoryTest.php | 2 +- .../testsuite/Magento/Ui/_files/bookmarks.php | 2 +- .../Magento/Ups/Model/CarrierTest.php | 2 +- .../Block/Catalog/Category/EditTest.php | 2 +- .../Block/Catalog/Category/TreeTest.php | 2 +- .../Block/Catalog/Edit/FormTest.php | 2 +- .../Block/Catalog/Product/EditTest.php | 2 +- .../Block/Catalog/Product/GridTest.php | 2 +- .../Block/Cms/Page/Edit/FormTest.php | 2 +- .../UrlRewrite/Block/Cms/Page/EditTest.php | 2 +- .../UrlRewrite/Block/Cms/Page/GridTest.php | 2 +- .../UrlRewrite/Block/Edit/FormTest.php | 3 +- .../Magento/UrlRewrite/Block/EditTest.php | 2 +- .../Magento/UrlRewrite/Block/SelectorTest.php | 2 +- .../Magento/User/Block/Role/Grid/UserTest.php | 2 +- .../Magento/User/Block/Role/Tab/EditTest.php | 2 +- .../User/Block/User/Edit/Tab/MainTest.php | 2 +- .../User/Controller/Adminhtml/AuthTest.php | 2 +- .../Controller/Adminhtml/Locks/GridTest.php | 2 +- .../Controller/Adminhtml/Locks/IndexTest.php | 2 +- .../Adminhtml/Locks/MassUnlockTest.php | 2 +- .../Controller/Adminhtml/User/DeleteTest.php | 2 +- .../Adminhtml/User/InvalidateTokenTest.php | 2 +- .../Controller/Adminhtml/User/RoleTest.php | 2 +- .../User/Controller/Adminhtml/UserTest.php | 2 +- .../Magento/User/Helper/DataTest.php | 4 +- .../Role/User/CollectionTest.php | 2 +- .../User/Model/ResourceModel/UserTest.php | 2 +- .../testsuite/Magento/User/Model/UserTest.php | 35 +- .../Magento/User/_files/dummy_user.php | 2 +- .../Magento/User/_files/locked_users.php | 2 +- .../Magento/User/_files/user_with_role.php | 2 +- .../Block/System/Variable/EditTest.php | 2 +- .../Adminhtml/System/VariableTest.php | 2 +- .../Variable/Model/Variable/ConfigTest.php | 2 +- .../Magento/Variable/Model/VariableTest.php | 2 +- .../Magento/Variable/_files/variable.php | 2 +- .../Model/PaymentTokenRepositoryTest.php | 67 +- .../Model/ResourceModel/PaymentTokenTest.php | 2 +- .../Magento/Vault/_files/customer.php | 2 +- .../Magento/Vault/_files/payment_tokens.php | 2 +- .../testsuite/Magento/Vault/_files/token.php | 26 + .../Version/Controller/Index/IndexTest.php | 2 +- .../Webapi/Controller/PathProcessorTest.php | 2 +- .../Webapi/Model/Config/ReaderTest.php | 2 +- .../Webapi/Model/Config/_files/webapi.php | 2 +- .../Webapi/Model/Config/_files/webapiA.xml | 2 +- .../Webapi/Model/Config/_files/webapiB.xml | 2 +- .../Webapi/Model/ServiceMetadataTest.php | 135 + .../Magento/Webapi/Model/Soap/ConfigTest.php | 115 + .../Webapi/Service/Entity/TestService.php | 2 +- .../Webapi/ServiceNameCollisionTest.php | 2 +- .../Magento/Webapi/_files/webapi_user.php | 2 +- .../Webapi/_files/webapi_user_rollback.php | 2 +- .../testsuite/Magento/Weee/Model/TaxTest.php | 2 +- .../Magento/Weee/_files/product_with_fpt.php | 2 +- .../Weee/_files/product_with_fpt_rollback.php | 2 +- .../Instance/Edit/Chooser/ContainerTest.php | 2 +- .../Edit/Chooser/DesignAbstractionTest.php | 2 +- .../Instance/Edit/Chooser/LayoutTest.php | 2 +- .../_files/design-abstraction_select.html | 2 +- .../child_page_with_inherited_containers.xml | 2 +- ...age_with_inherited_imported_containers.xml | 2 +- .../layout/child_page_with_own_containers.xml | 2 +- .../layout/child_page_without_containers.xml | 2 +- .../_files/layout/customer_account.xml | 2 +- .../non_page_handle_with_own_containers.xml | 2 +- .../Edit/Chooser/_files/layout/page_empty.xml | 2 +- .../root_page_with_imported_containers.xml | 2 +- .../layout/root_page_with_own_containers.xml | 2 +- .../layout/root_page_without_containers.xml | 2 +- .../root_page_without_own_containers.xml | 2 +- .../Chooser/_files/page_types_select.html | 2 +- .../Instance/Edit/Tab/Main/LayoutTest.php | 2 +- .../Widget/Instance/Edit/Tab/MainTest.php | 2 +- .../Adminhtml/Widget/Instance/EditTest.php | 2 +- .../Adminhtml/Widget/InstanceTest.php | 2 +- .../Controller/Adminhtml/WidgetTest.php | 2 +- .../Magento/Widget/Model/Config/DataTest.php | 2 +- .../Widget/Model/Config/ReaderTest.php | 2 +- .../_files/catalog_new_products_list.xml | 2 +- .../Config/_files/expectedGlobalArray.php | 2 +- .../_files/expectedGlobalDesignArray.php | 2 +- .../Config/_files/expectedMergedArray.php | 2 +- .../Config/_files/orders_and_returns.xml | 2 +- .../_files/orders_and_returns_customized.xml | 2 +- .../Widget/Model/Layout/UpdateTest.php | 2 +- .../Model/ResourceModel/Layout/UpdateTest.php | 2 +- .../Widget/Model/Template/FilterTest.php | 2 +- .../Widget/Model/Widget/ConfigTest.php | 2 +- .../Widget/Model/Widget/InstanceTest.php | 37 +- .../Magento/Widget/Model/WidgetTest.php | 2 +- .../adminhtml/magento_basic/composer.json | 2 +- .../design/adminhtml/magento_basic/theme.xml | 2 +- .../Magento/Widget/_files/layout_cache.php | 2 +- .../Magento/Widget/_files/layout_update.php | 2 +- .../Magento/Widget/_files/new_widget.php | 38 + .../Customer/Wishlist/Item/ColumnTest.php | 2 +- .../Customer/Wishlist/Item/OptionsTest.php | 2 +- .../Block/Customer/Wishlist/ItemsTest.php | 2 +- .../Wishlist/Block/Share/WishlistTest.php | 2 +- .../Magento/Wishlist/Controller/IndexTest.php | 2 +- .../Wishlist/Controller/SharedTest.php | 2 +- .../Magento/Wishlist/Helper/DataTest.php | 2 +- .../Magento/Wishlist/Model/ItemTest.php | 79 + .../ResourceModel/Item/CollectionTest.php | 74 + .../Magento/Wishlist/_files/wishlist.php | 2 +- .../Wishlist/_files/wishlist_shared.php | 2 +- .../wishlist_with_product_qty_increments.php | 2 +- .../JsTestDriver/framework/requirejs-util.js | 2 +- dev/tests/js/JsTestDriver/framework/stub.js | 2 +- .../js/JsTestDriver/jsTestDriver.php.dist | 2 +- .../js/JsTestDriver/jsTestDriverOrder.php | 2 +- dev/tests/js/JsTestDriver/run_js_tests.php | 42 +- .../testsuite/lib/ko/datepicker/datepicker.js | 4 +- .../testsuite/lib/ko/datepicker/index.html | 2 +- .../testsuite/lib/storage/index.html | 2 +- .../testsuite/lib/storage/test-storage.js | 4 +- .../testsuite/mage/_demo/index.html | 4 +- .../JsTestDriver/testsuite/mage/_demo/test.js | 4 +- .../testsuite/mage/accordion/accordion.js | 2 +- .../testsuite/mage/accordion/index.html | 4 +- .../testsuite/mage/button/button-test.js | 2 +- .../testsuite/mage/calendar/calendar-qunit.js | 4 +- .../testsuite/mage/calendar/calendar-test.js | 2 +- .../testsuite/mage/calendar/calendar.html | 4 +- .../mage/calendar/date-range-test.js | 4 +- .../testsuite/mage/collapsible/content.html | 4 +- .../testsuite/mage/collapsible/index.html | 2 +- .../mage/collapsible/test-collapsible.js | 2 +- .../testsuite/mage/decorate-test.js | 2 +- .../testsuite/mage/dropdown/index.html | 2 +- .../testsuite/mage/dropdown/test-dropdown.js | 2 +- .../mage/edit_trigger/edit-trigger-test.js | 2 +- .../testsuite/mage/form/form-test.js | 2 +- .../testsuite/mage/list/index.html | 4 +- .../testsuite/mage/list/jquery-list-test.js | 2 +- .../mage/loader/jquery-loader-test.js | 4 +- .../testsuite/mage/loader/loader-test.js | 2 +- .../testsuite/mage/loader/loader.html | 4 +- .../JsTestDriver/testsuite/mage/mage-test.js | 4 +- .../testsuite/mage/menu/index.html | 2 +- .../testsuite/mage/menu/test-menu.js | 2 +- .../mage/search/regular-search-test.js | 2 +- .../testsuite/mage/suggest/suggest-test.js | 2 +- .../mage/suggest/tree-suggest-test.js | 2 +- .../testsuite/mage/tabs/index.html | 2 +- .../testsuite/mage/tabs/tabs-test.js | 2 +- .../JsTestDriver/testsuite/mage/tabs/tabs.js | 2 +- .../mage/translate/translate-test.js | 2 +- .../translate_inline/translate-inline-test.js | 2 +- .../translate-inline-vde-dialog-test.js | 2 +- .../translate-inline-vde-test.js | 2 +- .../testsuite/mage/validation/index.html | 4 +- .../mage/validation/test-validation.js | 4 +- .../testsuite/mage/webapi-test.js | 2 +- .../testsuite/mage/zoom/zoom-test.js | 4 +- .../js/jasmine/assets/apply/components/fn.js | 2 +- dev/tests/js/jasmine/assets/apply/index.js | 2 +- .../jasmine/assets/apply/templates/node.html | 4 +- dev/tests/js/jasmine/assets/jsbuild/config.js | 2 +- .../js/jasmine/assets/jsbuild/external.js | 2 +- dev/tests/js/jasmine/assets/jsbuild/local.js | 2 +- dev/tests/js/jasmine/assets/script/index.js | 2 +- .../assets/script/templates/selector.html | 4 +- .../assets/script/templates/virtual.html | 2 +- dev/tests/js/jasmine/assets/text/config.js | 6 +- .../js/jasmine/assets/text/external.html | 2 +- dev/tests/js/jasmine/assets/text/local.html | 4 +- dev/tests/js/jasmine/assets/tools.js | 2 +- dev/tests/js/jasmine/require.conf.js | 14 +- dev/tests/js/jasmine/spec_runner/index.js | 42 +- .../js/jasmine/spec_runner/settings.json | 3 +- .../js/jasmine/spec_runner/tasks/connect.js | 4 +- .../js/jasmine/spec_runner/tasks/jasmine.js | 15 +- .../js/jasmine/spec_runner/template.html | 4 +- .../js/modal/modal-component.test.js | 127 + ...dynamic-rows-import-custom-options.test.js | 89 + .../utils/percentage-price-calculator.test.js | 67 + .../frontend/js/model/cart/cache.test.js | 114 + .../js/model/cart/estimate-service.test.js | 147 + .../cart/totals-processor/default.test.js | 146 + .../js/model/new-customer-address.test.js | 75 + .../js/view/cart/shipping-estimation.test.js | 115 + .../frontend/js/view/minicart.test.js | 92 + .../frontend/js/view/shipping.test.js | 194 + .../js/view/summary/cart-items.test.js | 106 + .../js/model/customer/address.test.js | 58 + .../js/view/authentication-popup.test.js | 82 + .../Magento/Msrp/frontend/js/msrp.test.js | 5 +- .../PageCache/frontend/js/page-cache.test.js | 18 +- .../frontend/web/js/process-review.test.js | 76 + .../Magento/Ui/base/js/core/layout.test.js | 13 +- .../Magento/Ui/base/js/form/adapter.test.js | 5 +- .../Magento/Ui/base/js/form/client.test.js | 132 +- .../Ui/base/js/form/components/area.test.js | 17 +- .../js/form/components/collection.test.js | 46 +- .../form/components/collection/item.test.js | 54 +- .../Ui/base/js/form/components/group.test.js | 20 +- .../Ui/base/js/form/components/html.test.js | 20 +- .../Ui/base/js/form/components/tab.test.js | 37 +- .../base/js/form/components/tab_group.test.js | 100 +- .../Ui/base/js/form/element/abstract.test.js | 41 +- .../Ui/base/js/form/element/boolean.test.js | 2 +- .../Ui/base/js/form/element/date-time.test.js | 41 + .../Ui/base/js/form/element/date.test.js | 40 +- .../js/form/element/file-uploader.test.js | 2 +- .../base/js/form/element/multiselect.test.js | 78 +- .../Ui/base/js/form/element/post-code.test.js | 3 +- .../Ui/base/js/form/element/region.test.js | 3 +- .../Ui/base/js/form/element/select.test.js | 96 +- .../Ui/base/js/form/element/textarea.test.js | 4 +- .../code/Magento/Ui/base/js/form/form.test.js | 61 +- .../Magento/Ui/base/js/form/provider.test.js | 20 +- .../Magento/Ui/base/js/form/ui-select.test.js | 177 +- .../Ui/base/js/grid/columns/actions.test.js | 24 +- .../Ui/base/js/grid/columns/column.test.js | 14 +- .../Ui/base/js/grid/columns/date.test.js | 24 +- .../base/js/grid/columns/multiselect.test.js | 128 +- .../Ui/base/js/grid/columns/select.test.js | 14 +- .../grid/controls/bookmarks/bookmarks.test.js | 104 - .../grid/controls/bookmarks/storage.test.js | 8 +- .../js/grid/controls/bookmarks/view.test.js | 67 - .../Ui/base/js/grid/controls/columns.test.js | 22 +- .../Ui/base/js/grid/data-storage.test.js | 76 + .../Ui/base/js/grid/editing/bulk.test.js | 8 +- .../Ui/base/js/grid/filters/filters.test.js | 29 +- .../Ui/base/js/grid/filters/range.test.js | 30 +- .../Ui/base/js/grid/paging/paging.test.js | 112 +- .../Magento/Ui/base/js/grid/resize.test.js | 185 +- .../Ui/base/js/grid/search/search.test.js | 22 +- .../Ui/base/js/grid/sticky/sticky.test.js | 44 +- .../Ui/base/js/grid/tree-massactions.test.js | 8 +- .../Ui/base/js/lib/component/core.test.js | 71 - .../Ui/base/js/lib/component/links.test.js | 6 +- .../Ui/base/js/lib/component/manip.test.js | 67 - .../Ui/base/js/lib/component/provider.test.js | 42 - .../base/js/lib/component/traversal.test.js | 22 - .../Magento/Ui/base/js/lib/events.test.js | 3 +- .../Ui/base/js/lib/ko/bind/datepicker.test.js | 45 +- .../Ui/base/js/lib/ko/bind/i18n.test.js | 43 +- .../Ui/base/js/lib/registry/events.test.js | 58 - .../Ui/base/js/lib/registry/registry.test.js | 54 +- .../Ui/base/js/lib/registry/storage.test.js | 122 - .../Magento/Ui/base/js/modal/alert.test.js | 2 +- .../Magento/Ui/base/js/modal/confirm.test.js | 2 +- .../Magento/Ui/base/js/modal/modal.test.js | 2 +- .../Magento/Ui/base/js/modal/prompt.test.js | 2 +- .../js/jasmine/tests/lib/mage/apply.test.js | 4 +- .../js/jasmine/tests/lib/mage/gallery.test.js | 154 - .../js/jasmine/tests/lib/mage/misc.test.js | 651 ++ .../lib/mage/requirejs/static-jsbuild.test.js | 4 +- .../lib/mage/requirejs/static-text.test.js | 6 +- .../lib/mage/requirejs/statistician.test.js | 3 +- .../js/jasmine/tests/lib/mage/scripts.test.js | 3 +- .../jasmine/tests/lib/mage/template.test.js | 3 +- .../jasmine/tests/lib/mage/validation.test.js | 2 +- .../Rule/Design/FinalImplementation.php | 30 + .../Rule/Design/FinalImplementationTest.php | 128 + .../resources/rulesets/design.xml | 36 + .../Sniffs/Arrays/ShortArraySyntaxSniff.php | 2 +- .../Magento/Sniffs/Files/LineLengthSniff.php | 2 +- .../Magento/Sniffs/Less/AvoidIdSniff.php | 2 +- .../Sniffs/Less/BracesFormattingSniff.php | 2 +- .../Magento/Sniffs/Less/ClassNamingSniff.php | 2 +- .../Magento/Sniffs/Less/ColonSpacingSniff.php | 2 +- .../Sniffs/Less/ColourDefinitionSniff.php | 2 +- .../Less/CombinatorIndentationSniff.php | 2 +- .../Sniffs/Less/CommentLevelsSniff.php | 2 +- .../Sniffs/Less/ImportantPropertySniff.php | 2 +- .../Magento/Sniffs/Less/IndentationSniff.php | 2 +- .../Sniffs/Less/PropertiesLineBreakSniff.php | 2 +- .../Sniffs/Less/PropertiesSortingSniff.php | 2 +- .../Magento/Sniffs/Less/QuotesSniff.php | 2 +- .../Sniffs/Less/SelectorDelimiterSniff.php | 2 +- .../Sniffs/Less/SemicolonSpacingSniff.php | 2 +- .../Sniffs/Less/TokenizerSymbolsInterface.php | 2 +- .../Less/TypeSelectorConcatenationSniff.php | 2 +- .../Sniffs/Less/TypeSelectorsSniff.php | 2 +- .../Magento/Sniffs/Less/VariablesSniff.php | 2 +- .../Magento/Sniffs/Less/ZeroUnitsSniff.php | 2 +- .../LiteralNamespacesSniff.php | 2 +- .../Sniffs/MicroOptimizations/IsNullSniff.php | 2 +- .../NamingConventions/InterfaceNameSniff.php | 2 +- .../NamingConventions/ReservedWordsSniff.php | 79 +- .../Sniffs/Translation/ConstantUsageSniff.php | 69 +- .../Whitespace/EmptyLineMissedSniff.php | 2 +- .../Whitespace/MultipleEmptyLinesSniff.php | 2 +- .../Tool/BlacklistInterface.php | 2 +- .../CodingStandard/Tool/CodeMessDetector.php | 2 +- .../CodingStandard/Tool/CodeSniffer.php | 2 +- .../Tool/CodeSniffer/LessWrapper.php | 2 +- .../Tool/CodeSniffer/Wrapper.php | 2 +- .../CodingStandard/Tool/CopyPasteDetector.php | 34 +- .../Tool/ExtensionInterface.php | 2 +- .../CodingStandard/ToolInterface.php | 2 +- .../TestFramework/Dependency/DbRule.php | 2 +- .../TestFramework/Dependency/DiRule.php | 2 +- .../TestFramework/Dependency/LayoutRule.php | 2 +- .../TestFramework/Dependency/PhpRule.php | 2 +- .../Dependency/RuleInterface.php | 2 +- .../VirtualType/VirtualTypeMapper.php | 2 +- .../Inspection/AbstractCommand.php | 2 +- .../TestFramework/Inspection/Exception.php | 2 +- .../Inspection/JsHint/Command.php | 2 +- .../TestFramework/Inspection/WordsFinder.php | 4 +- .../Integrity/AbstractConfig.php | 6 +- .../Integrity/Library/Injectable.php | 2 +- .../DependenciesCollectorInterface.php | 2 +- .../Library/PhpParser/ParserFactory.php | 2 +- .../Library/PhpParser/ParserInterface.php | 2 +- .../Library/PhpParser/StaticCalls.php | 2 +- .../Integrity/Library/PhpParser/Throws.php | 2 +- .../Integrity/Library/PhpParser/Tokens.php | 2 +- .../Integrity/Library/PhpParser/Uses.php | 2 +- .../Integrity/PluginValidator.php | 2 +- .../TestFramework/Utility/ChangedFiles.php | 64 +- .../TestFramework/Utility/CodeCheck.php | 6 +- .../Magento/TestFramework/Utility/File.php | 80 + .../Utility/File/RegexIteratorFactory.php | 26 + .../Utility/FunctionDetector.php | 113 + .../Utility/XssOutputValidator.php | 2 +- .../static/framework/Magento/ruleset.xml | 2 +- dev/tests/static/framework/autoload.php | 9 +- dev/tests/static/framework/bootstrap.php | 2 +- .../framework/tests/unit/phpunit.xml.dist | 2 +- .../Translation/ConstantUsageSniffTest.php | 96 + .../Translation/_files/correct_arguments.txt | 29 + .../_files/incorrect_arguments.txt | 27 + .../Test/Integrity/Library/InjectableTest.php | 2 +- .../Library/PhpParser/ParserFactoryTest.php | 2 +- .../Library/PhpParser/StaticCallsTest.php | 2 +- .../Library/PhpParser/ThrowsTest.php | 2 +- .../Library/PhpParser/TokensTest.php | 2 +- .../Integrity/Library/PhpParser/UsesTest.php | 2 +- .../Test/Utility/File/CodeCheckTest.php | 77 + ...ss_name_in_namespace_and_variable_name.txt | 8 + .../File/_files/create_new_instance.txt | 7 + .../File/_files/create_new_instance2.txt | 7 + .../File/_files/create_new_instance3.txt | 7 + .../Test/Utility/File/_files/extends.txt | 8 + .../Test/Utility/File/_files/extends2.txt | 8 + .../Test/Utility/File/_files/implements.txt | 8 + .../Test/Utility/File/_files/implements2.txt | 8 + .../Magento/Test/Utility/File/_files/use.txt | 8 + .../Magento/Test/Utility/File/_files/use2.txt | 8 + .../Tool/CodeMessDetectorTest.php | 2 +- .../Tool/CodeSniffer/WrapperTest.php | 2 +- .../CodingStandard/Tool/CodeSnifferTest.php | 2 +- .../TestFramework/Dependency/DbRuleTest.php | 2 +- .../TestFramework/Dependency/DiRuleTest.php | 2 +- .../Dependency/LayoutRuleTest.php | 2 +- .../TestFramework/Dependency/PhpRuleTest.php | 2 +- .../VirtualType/VirtualTypeMapperTest.php | 2 +- .../VirtualType/_files/etc/adminhtml/di.xml | 2 +- .../Dependency/VirtualType/_files/etc/di.xml | 2 +- .../_files/di_external_dependency.xml | 2 +- .../_files/di_in_module_dependency.xml | 2 +- .../Dependency/_files/di_no_dependency.xml | 2 +- .../_files/di_virtual_dependency.xml | 2 +- .../Dependency/_files/layout_handle.xml | 2 +- .../_files/layout_handle_parent.xml | 2 +- .../_files/layout_handle_update.xml | 2 +- .../Dependency/_files/layout_reference.xml | 2 +- .../Inspection/JsHint/CommandTest.php | 2 +- .../Inspection/WordsFinderTest.php | 2 +- .../Inspection/_files/broken_config.xml | 2 +- .../Inspection/_files/config.xml | 2 +- .../Inspection/_files/config_additional.xml | 2 +- .../_files/empty_whitelist_path.xml | 2 +- .../Inspection/_files/empty_words_config.xml | 2 +- .../Inspection/_files/words_finder/buffy.php | 2 +- .../interview_with_the_vampire.php | 2 +- .../words_finder/self_tested_config.xml | 2 +- .../_files/words_finder/twilight/eclipse.php | 2 +- .../_files/words_finder/twilight/newmoon.php | 2 +- .../TestFramework/Utility/FileTest.php | 83 + .../Utility/FunctionDetectorTest.php | 22 + .../Utility/XssOutputValidatorTest.php | 2 +- .../TestFramework/Utility/_files/test.txt | 9 + .../Utility/_files/xss_safe.phtml | 2 +- .../Utility/_files/xss_unsafe.phtml | 2 +- dev/tests/static/get_github_changes.php | 112 +- dev/tests/static/phpunit-all.xml.dist | 2 +- dev/tests/static/phpunit.xml.dist | 2 +- .../Test/Integrity/ApiAnnotationTest.php | 40 - .../App/Language/CircularDependencyTest.php | 2 +- .../Integrity/App/Language/ConfigTest.php | 2 +- .../App/Language/TranslationFiles.php | 2 +- .../App/Language/TranslationFilesTest.php | 2 +- .../App/Language/_files/known_invalid.xml | 2 +- .../App/Language/_files/known_valid.xml | 2 +- .../Test/Integrity/CircularDependencyTest.php | 2 +- .../Magento/Test/Integrity/ClassesTest.php | 2 +- .../Test/Integrity/ComposerLockTest.php | 2 +- .../Magento/Test/Integrity/ComposerTest.php | 2 +- .../Magento/Test/Integrity/ConfigTest.php | 2 +- .../Magento/Test/Integrity/DependencyTest.php | 2 +- .../Test/Integrity/Di/CompilerTest.php | 6 +- .../Test/Integrity/ExceptionHierarchyTest.php | 2 +- .../Test/Integrity/HhvmCompatibilityTest.php | 11 +- .../Test/Integrity/Layout/BlocksTest.php | 2 +- .../Test/Integrity/Layout/HandlesTest.php | 2 +- .../Integrity/Layout/ThemeHandlesTest.php | 2 +- .../Test/Integrity/Library/DependencyTest.php | 43 +- .../Magento/Backend/SystemConfigTest.php | 2 +- .../Model/Fieldset/FieldsetConfigTest.php | 2 +- .../Core/Model/Fieldset/_files/fieldset.xml | 2 +- .../Model/Fieldset/_files/fieldset_file.xml | 2 +- .../Fieldset/_files/invalid_fieldset.xml | 2 +- .../Api/ExtensibleInterfacesTest.php | 2 +- .../Framework/Search/RequestConfigTest.php | 4 +- .../Search/SearchEngineConfigTest.php | 2 +- .../Search/_files/request/invalid.xml | 2 +- .../Search/_files/request/invalid_partial.xml | 2 +- .../Framework/Search/_files/request/valid.xml | 4 +- .../Search/_files/request/valid_partial.xml | 4 +- .../Search/_files/search_engine/invalid.xml | 2 +- .../Search/_files/search_engine/valid.xml | 2 +- .../Integrity/Magento/Indexer/ConfigTest.php | 2 +- .../Magento/Indexer/_files/invalid.xml | 2 +- .../Magento/Indexer/_files/valid.xml | 2 +- .../Magento/Indexer/_files/valid_partial.xml | 2 +- .../Payment/Config/ReferentialTest.php | 2 +- .../Magento/Payment/Model/ConfigTest.php | 2 +- .../Payment/Model/_files/invalid_payment.xml | 2 +- .../Model/_files/invalid_payment_partial.xml | 2 +- .../Magento/Payment/Model/_files/payment.xml | 2 +- .../Payment/Model/_files/payment_partial.xml | 2 +- .../Magento/Persistent/ConfigTest.php | 2 +- .../Persistent/_files/invalid_persistent.xml | 2 +- .../Persistent/_files/valid_persistent.xml | 2 +- .../Magento/Webapi/Model/ConfigTest.php | 2 +- .../Webapi/Model/_files/invalid_webapi.xml | 2 +- .../Magento/Webapi/Model/_files/webapi.xml | 2 +- .../Magento/Widget/WidgetConfigTest.php | 2 +- .../Magento/Widget/_files/invalid_widget.xml | 2 +- .../Magento/Widget/_files/widget.xml | 2 +- .../Magento/Widget/_files/widget_file.xml | 4 +- .../Integrity/ObserverImplementationTest.php | 36 +- .../Integrity/Phrase/AbstractTestCase.php | 2 +- .../Test/Integrity/Phrase/ArgumentsTest.php | 2 +- .../Integrity/Phrase/Legacy/SignatureTest.php | 2 +- .../Test/Integrity/Readme/ReadmeTest.php | 2 +- .../Test/Integrity/TestPlacementTest.php | 2 +- .../Magento/Test/Integrity/Xml/SchemaTest.php | 2 +- .../_files/blacklist/exception_hierarchy.txt | 3 + .../Integrity/_files/blacklist/observers.txt | 16 + .../Integrity/_files/blacklist/reference.txt | 1 + .../_files/dependency_test/tables_ce.php | 2 +- .../Magento/Test/Js/Exemplar/JsHintTest.php | 2 +- .../Magento/Test/Js/LiveCodeTest.php | 2 +- .../Test/Js/_files/blacklist/magento.txt | 1026 +-- .../Test/Js/_files/eslint/.eslintrc-magento | 5 +- .../Magento/Test/Legacy/ClassesTest.php | 2 +- .../Magento/Test/Legacy/ConfigTest.php | 2 +- .../Magento/Test/Legacy/CopyrightTest.php | 20 +- .../Magento/Test/Legacy/EmailTemplateTest.php | 2 +- .../Magento/Test/Legacy/FilesystemTest.php | 2 +- .../Test/Legacy/InstallUpgradeTest.php | 2 +- .../Magento/Test/Legacy/LayoutTest.php | 2 +- .../Test/Legacy/LibraryLocationTest.php | 2 +- .../Magento/Test/Legacy/LicenseTest.php | 2 +- .../Magento/Core/Block/AbstractBlockTest.php | 2 +- .../Framework/Module/ModuleXMLTest.php | 2 +- .../Framework/ObjectManager/DiConfigTest.php | 2 +- .../Test/Legacy/Magento/Widget/XmlTest.php | 2 +- .../Test/Legacy/ModuleDBChangeTest.php | 62 +- .../Magento/Test/Legacy/ObsoleteAclTest.php | 2 +- .../Magento/Test/Legacy/ObsoleteCodeTest.php | 7 +- .../Test/Legacy/ObsoleteConnectionTest.php | 2 +- .../Magento/Test/Legacy/ObsoleteMenuTest.php | 2 +- .../Test/Legacy/ObsoleteResponseTest.php | 2 +- .../ObsoleteSystemConfigurationTest.php | 2 +- .../Test/Legacy/ObsoleteThemeLocalXmlTest.php | 2 +- .../Magento/Test/Legacy/PhtmlTemplateTest.php | 2 +- .../Test/Legacy/RestrictedCodeTest.php | 46 +- .../Magento/Test/Legacy/TableTest.php | 2 +- .../Legacy/UnsecureFunctionsUsageTest.php | 222 + .../Magento/Test/Legacy/WordsTest.php | 2 +- .../Legacy/_files/blacklist/obsolete_mage.php | 2 +- .../connection/blacklist/files_list.php | 2 +- .../whitelist/refactored_modules.php | 2 +- .../Legacy/_files/copyright/blacklist.php | 2 +- .../Test/Legacy/_files/obsolete_classes.php | 7 +- .../Legacy/_files/obsolete_config_nodes.php | 2 +- .../Test/Legacy/_files/obsolete_constants.php | 9 +- .../Test/Legacy/_files/obsolete_methods.php | 3 +- .../Legacy/_files/obsolete_namespaces.php | 2 +- .../Test/Legacy/_files/obsolete_paths.php | 2 +- .../Legacy/_files/obsolete_properties.php | 2 +- .../_files/response/blacklist/files_list.php | 2 +- .../response/obsolete_response_methods.php | 2 +- .../response/whitelist/refactored_modules.php | 2 +- .../Test/Legacy/_files/restricted_classes.php | 206 +- .../security/unsecure_php_functions.php | 52 + .../Test/Legacy/_files/security/whitelist.txt | 5 + .../Magento/Test/Legacy/_files/words_ce.xml | 2 +- .../Magento/Test/Less/LiveCodeTest.php | 2 +- .../Test/Less/_files/lesscs/ruleset.xml | 4 +- .../Magento/Test/Php/LiveCodeTest.php | 116 +- .../Magento/Test/Php/XssPhtmlTemplateTest.php | 2 +- .../Php/_files/phpcpd/blacklist/common.txt | 45 +- .../Magento/Test/Php/_files/phpcs/ruleset.xml | 2 +- .../Magento/Test/Php/_files/phpmd/ruleset.xml | 7 +- .../_files/whitelist/exempt_modules/ce.php | 6 +- .../Composer/RootComposerMappingTest.php | 2 +- dev/tests/unit/framework/autoload.php | 6 +- dev/tests/unit/framework/bootstrap.php | 2 +- dev/tests/unit/phpunit.xml.dist | 4 +- .../Layout/processors/addItemRenderer.xsl | 2 +- .../Layout/processors/addToParentGroup.xsl | 2 +- .../Tools/Layout/processors/headBlocks.xsl | 2 +- .../Layout/processors/layoutArguments.xsl | 2 +- .../Layout/processors/layoutGridContainer.xsl | 2 +- .../Tools/Layout/processors/layoutHandles.xsl | 2 +- .../Tools/Layout/processors/layoutLabels.xsl | 2 +- .../Layout/processors/layoutReferences.xsl | 2 +- .../Layout/processors/layoutTranslate.xsl | 2 +- dev/tools/bootstrap.php | 2 +- dev/tools/grunt/assets/legacy-build/shim.js | 2 +- dev/tools/grunt/configs/clean.js | 14 +- dev/tools/grunt/configs/combo.js | 2 +- dev/tools/grunt/configs/eslint.js | 2 +- dev/tools/grunt/configs/exec.js | 2 +- dev/tools/grunt/configs/imagemin.js | 2 +- dev/tools/grunt/configs/jscs.js | 2 +- dev/tools/grunt/configs/less.js | 2 +- dev/tools/grunt/configs/path.js | 3 +- dev/tools/grunt/configs/replace.js | 4 +- dev/tools/grunt/configs/themes.js | 2 +- dev/tools/grunt/configs/usebanner.js | 4 +- dev/tools/grunt/configs/watch.js | 2 +- dev/tools/grunt/tasks/black-list-generator.js | 4 +- dev/tools/grunt/tasks/clean-black-list.js | 2 +- dev/tools/grunt/tasks/deploy.js | 2 +- dev/tools/grunt/tasks/mage-minify.js | 2 +- dev/tools/grunt/tasks/static.js | 2 +- .../grunt/tools/collect-validation-files.js | 4 +- dev/tools/grunt/tools/files-router.js | 2 +- dev/tools/grunt/tools/fs-tools.js | 2 +- dev/travis/before_install.sh | 2 +- dev/travis/before_script.sh | 21 +- generated/.htaccess | 2 + index.php | 2 +- lib/internal/Magento/Framework/Acl.php | 2 +- .../Magento/Framework/Acl/AclResource.php | 2 +- .../Acl/AclResource/Config/Converter/Dom.php | 2 +- .../AclResource/Config/Reader/Filesystem.php | 2 +- .../Acl/AclResource/Config/SchemaLocator.php | 2 +- .../Framework/Acl/AclResource/Provider.php | 52 +- .../Acl/AclResource/ProviderInterface.php | 2 +- .../Framework/Acl/AclResource/TreeBuilder.php | 2 +- .../Framework/Acl/AclResourceFactory.php | 2 +- .../Magento/Framework/Acl/Builder.php | 29 +- lib/internal/Magento/Framework/Acl/Cache.php | 8 +- .../Magento/Framework/Acl/CacheInterface.php | 4 +- .../Magento/Framework/Acl/Data/Cache.php | 108 + .../Framework/Acl/Data/CacheInterface.php | 11 + .../Framework/Acl/Loader/DefaultLoader.php | 2 +- .../Framework/Acl/Loader/ResourceLoader.php | 2 +- .../Magento/Framework/Acl/LoaderInterface.php | 2 +- .../Magento/Framework/Acl/Role/Registry.php | 2 +- .../Magento/Framework/Acl/RootResource.php | 2 +- .../AclResource/Config/Converter/DomTest.php | 2 +- .../Converter/_files/converted_valid_acl.php | 2 +- .../Config/Converter/_files/valid_acl.xml | 2 +- .../Unit/AclResource/Config/MergedXsdTest.php | 2 +- .../AclResource/Config/SchemaLocatorTest.php | 2 +- .../Test/Unit/AclResource/Config/XsdTest.php | 2 +- .../Config/_files/invalidAclXmlArray.php | 2 +- .../_files/invalidMergedAclXmlArray.php | 2 +- .../AclResource/Config/_files/valid_acl.xml | 2 +- .../Config/_files/valid_merged_acl.xml | 2 +- .../Test/Unit/AclResource/ProviderTest.php | 66 +- .../Test/Unit/AclResource/TreeBuilderTest.php | 2 +- .../Framework/Acl/Test/Unit/BuilderTest.php | 30 +- .../Framework/Acl/Test/Unit/CacheTest.php | 2 +- .../Acl/Test/Unit/Loader/DefaultTest.php | 2 +- .../Test/Unit/Loader/ResourceLoaderTest.php | 2 +- .../Acl/Test/Unit/ResourceFactoryTest.php | 2 +- .../Acl/Test/Unit/Role/RegistryTest.php | 2 +- .../Acl/Test/Unit/_files/resourceList.php | 2 +- .../Framework/Acl/Test/Unit/_files/result.php | 2 +- .../Magento/Framework/Acl/etc/acl.xsd | 2 +- .../Magento/Framework/Acl/etc/acl_merged.xsd | 2 +- lib/internal/Magento/Framework/AclFactory.php | 2 +- .../Api/AbstractExtensibleObject.php | 2 +- .../Api/AbstractServiceCollection.php | 2 +- .../Framework/Api/AbstractSimpleObject.php | 2 +- .../Api/AbstractSimpleObjectBuilder.php | 2 +- .../Framework/Api/ArrayObjectSearch.php | 2 +- .../Framework/Api/AttributeInterface.php | 2 +- .../Framework/Api/AttributeMetadata.php | 2 +- .../Api/AttributeTypeResolverInterface.php | 2 +- .../Magento/Framework/Api/AttributeValue.php | 2 +- .../Framework/Api/AttributeValueFactory.php | 2 +- .../ExtensionAttributesGenerator.php | 2 +- .../ExtensionAttributesInterfaceGenerator.php | 2 +- .../Framework/Api/Code/Generator/Mapper.php | 2 +- .../Api/Code/Generator/SearchResults.php | 2 +- .../Framework/Api/CriteriaInterface.php | 2 +- .../Api/CustomAttributesDataInterface.php | 2 +- .../Api/Data/ImageContentInterface.php | 2 +- .../Api/Data/VideoContentInterface.php | 2 +- .../Framework/Api/DataObjectHelper.php | 2 +- .../Framework/Api/DefaultMetadataService.php | 2 +- .../Framework/Api/ExtensibleDataInterface.php | 2 +- .../Api/ExtensibleDataObjectConverter.php | 2 +- .../Api/ExtensionAttribute/Config.php | 16 +- .../ExtensionAttribute/Config/Converter.php | 2 +- .../Api/ExtensionAttribute/Config/Reader.php | 2 +- .../Config/SchemaLocator.php | 2 +- .../Api/ExtensionAttribute/JoinData.php | 2 +- .../ExtensionAttribute/JoinDataInterface.php | 2 +- .../JoinDataInterfaceFactory.php | 2 +- .../Api/ExtensionAttribute/JoinProcessor.php | 2 +- .../JoinProcessorHelper.php | 2 +- .../JoinProcessorInterface.php | 2 +- .../Api/ExtensionAttributesFactory.php | 2 +- .../Api/ExtensionAttributesInterface.php | 2 +- lib/internal/Magento/Framework/Api/Filter.php | 2 +- .../Magento/Framework/Api/FilterBuilder.php | 2 +- .../Magento/Framework/Api/ImageContent.php | 2 +- .../Framework/Api/ImageContentValidator.php | 2 +- .../Api/ImageContentValidatorInterface.php | 2 +- .../Magento/Framework/Api/ImageProcessor.php | 2 +- .../Framework/Api/ImageProcessorInterface.php | 2 +- .../Framework/Api/MetadataObjectInterface.php | 2 +- .../Api/MetadataServiceInterface.php | 2 +- .../Magento/Framework/Api/ObjectFactory.php | 2 +- .../Api/Search/AggregationInterface.php | 2 +- .../Api/Search/AggregationValueInterface.php | 2 +- .../Framework/Api/Search/BucketInterface.php | 2 +- .../Magento/Framework/Api/Search/Document.php | 2 +- .../Framework/Api/Search/DocumentFactory.php | 2 +- .../Api/Search/DocumentInterface.php | 2 +- .../Framework/Api/Search/FilterGroup.php | 2 +- .../Api/Search/FilterGroupBuilder.php | 2 +- .../Api/Search/ReportingInterface.php | 2 +- .../Framework/Api/Search/SearchCriteria.php | 2 +- .../Api/Search/SearchCriteriaBuilder.php | 2 +- .../Api/Search/SearchCriteriaFactory.php | 2 +- .../Api/Search/SearchCriteriaInterface.php | 2 +- .../Framework/Api/Search/SearchInterface.php | 2 +- .../Framework/Api/Search/SearchResult.php | 16 +- .../Api/Search/SearchResultFactory.php | 2 +- .../Api/Search/SearchResultInterface.php | 14 +- .../Magento/Framework/Api/SearchCriteria.php | 2 +- .../SearchCriteria/CollectionProcessor.php | 2 +- .../CollectionProcessor/FilterProcessor.php | 2 +- .../FilterProcessor/CustomFilterInterface.php | 2 +- .../CollectionProcessor/JoinProcessor.php | 2 +- .../JoinProcessor/CustomJoinInterface.php | 2 +- .../PaginationProcessor.php | 2 +- .../CollectionProcessor/SortingProcessor.php | 2 +- .../CollectionProcessorInterface.php | 2 +- .../Framework/Api/SearchCriteriaBuilder.php | 2 +- .../Framework/Api/SearchCriteriaInterface.php | 2 +- .../Magento/Framework/Api/SearchResults.php | 2 +- .../Framework/Api/SearchResultsInterface.php | 2 +- .../Framework/Api/SimpleBuilderInterface.php | 2 +- .../Api/SimpleDataObjectConverter.php | 2 +- .../Magento/Framework/Api/SortOrder.php | 2 +- .../Framework/Api/SortOrderBuilder.php | 2 +- .../Unit/Api/ImageContentValidatorTest.php | 2 +- .../Api/Test/Unit/Api/ImageProcessorTest.php | 2 +- .../Generator/EntityChildTestAbstract.php | 2 +- .../Unit/Code/Generator/ExtensibleSample.php | 2 +- .../Generator/ExtensibleSampleInterface.php | 2 +- .../ExtensionAttributesGeneratorTest.php | 2 +- ...ensionAttributesInterfaceGeneratorTest.php | 2 +- .../Code/Generator/GenerateMapperTest.php | 2 +- .../Generator/GenerateSearchResultsTest.php | 2 +- .../Api/Test/Unit/Code/Generator/Sample.php | 2 +- .../Api/Test/Unit/Data/AttributeValueTest.php | 2 +- .../Api/Test/Unit/DataObjectHelperTest.php | 2 +- .../ExtensibleDataObjectConverterTest.php | 2 +- .../Config/ConverterTest.php | 2 +- .../ExtensionAttribute/Config/ReaderTest.php | 2 +- .../Config/SchemaLocatorTest.php | 2 +- .../ExtensionAttribute/Config/XsdTest.php | 2 +- .../Config/_files/extension_attributes.xml | 2 +- ...ension_attributes_with_join_directives.xml | 2 +- .../Api/Test/Unit/Search/SearchResultTest.php | 72 + .../FilterProcessorTest.php | 2 +- .../CollectionProcessor/JoinProcessorTest.php | 2 +- .../PaginationProcessorTest.php | 2 +- .../SortingProcessorTest.php | 2 +- .../CollectionProcessorTest.php | 2 +- .../Framework/Api/Test/Unit/SortOrderTest.php | 2 +- .../Test/Unit/StubAbstractSimpleObject.php | 2 +- .../Magento/Framework/Api/Uploader.php | 2 +- .../Api/etc/extension_attributes.xsd | 2 +- .../Framework/App/Action/AbstractAction.php | 2 +- .../Magento/Framework/App/Action/Action.php | 2 +- .../Magento/Framework/App/Action/Context.php | 2 +- .../Magento/Framework/App/Action/Forward.php | 2 +- .../Framework/App/Action/Plugin/Design.php | 2 +- .../Magento/Framework/App/Action/Redirect.php | 2 +- .../Magento/Framework/App/ActionFactory.php | 2 +- .../Magento/Framework/App/ActionFlag.php | 2 +- .../Magento/Framework/App/ActionInterface.php | 2 +- lib/internal/Magento/Framework/App/Area.php | 2 +- .../App/Area/FrontNameResolverFactory.php | 2 +- .../App/Area/FrontNameResolverInterface.php | 2 +- .../Magento/Framework/App/AreaInterface.php | 2 +- .../Magento/Framework/App/AreaList.php | 2 +- .../Magento/Framework/App/AreaList/Proxy.php | 2 +- .../App/Arguments/ArgumentInterpreter.php | 2 +- .../App/Arguments/FileResolver/Primary.php | 2 +- .../App/Arguments/ValidationState.php | 2 +- .../Magento/Framework/App/Bootstrap.php | 4 +- lib/internal/Magento/Framework/App/Cache.php | 2 +- .../Framework/App/Cache/FlushCacheByTags.php | 23 +- .../Framework/App/Cache/Frontend/Factory.php | 2 +- .../Framework/App/Cache/Frontend/Pool.php | 2 +- .../Framework/App/Cache/InstanceFactory.php | 2 +- .../Magento/Framework/App/Cache/Manager.php | 2 +- .../Magento/Framework/App/Cache/Proxy.php | 2 +- .../Magento/Framework/App/Cache/State.php | 2 +- .../Framework/App/Cache/StateInterface.php | 2 +- .../Framework/App/Cache/Tag/Resolver.php | 45 + .../App/Cache/Tag/Strategy/Dummy.php | 26 + .../App/Cache/Tag/Strategy/Factory.php | 83 + .../App/Cache/Tag/Strategy/Identifier.php | 30 + .../App/Cache/Tag/StrategyInterface.php | 21 + .../Framework/App/Cache/Type/AccessProxy.php | 2 +- .../Framework/App/Cache/Type/Block.php | 2 +- .../Framework/App/Cache/Type/Collection.php | 2 +- .../Framework/App/Cache/Type/Config.php | 2 +- .../Framework/App/Cache/Type/Dummy.php | 67 + .../Framework/App/Cache/Type/FrontendPool.php | 2 +- .../Framework/App/Cache/Type/Layout.php | 2 +- .../Framework/App/Cache/Type/Reflection.php | 2 +- .../Framework/App/Cache/Type/Translate.php | 2 +- .../Magento/Framework/App/Cache/TypeList.php | 19 +- .../Framework/App/Cache/TypeListInterface.php | 2 +- .../Magento/Framework/App/CacheInterface.php | 2 +- lib/internal/Magento/Framework/App/Config.php | 94 +- .../Magento/Framework/App/Config/Base.php | 2 +- .../Framework/App/Config/BaseFactory.php | 2 +- .../Framework/App/Config/CommentInterface.php | 21 + .../App/Config/CommentParserInterface.php | 23 + .../App/Config/ConfigPathResolver.php | 66 + .../Config/ConfigResource/ConfigInterface.php | 2 +- .../App/Config/ConfigSourceAggregated.php | 61 + .../App/Config/ConfigSourceInterface.php | 37 + .../App/Config/ConfigTypeInterface.php | 27 + .../Magento/Framework/App/Config/Data.php | 2 +- .../App/Config/Data/ProcessorFactory.php | 2 +- .../App/Config/Data/ProcessorInterface.php | 2 +- .../Framework/App/Config/DataFactory.php | 2 +- .../Framework/App/Config/DataInterface.php | 2 +- .../Magento/Framework/App/Config/Element.php | 2 +- .../Framework/App/Config/FileResolver.php | 2 +- .../Magento/Framework/App/Config/Initial.php | 22 +- .../App/Config/Initial/Converter.php | 2 +- .../Framework/App/Config/Initial/Reader.php | 2 +- .../App/Config/Initial/SchemaLocator.php | 2 +- .../App/Config/InitialConfigSource.php | 56 + .../Config/MetadataConfigTypeProcessor.php | 116 + .../App/Config/MetadataProcessor.php | 2 +- .../Config/MutableScopeConfigInterface.php | 2 +- .../App/Config/PostProcessorComposite.php | 39 + .../App/Config/PreProcessorComposite.php | 40 + .../Config/Reader/Source/SourceInterface.php | 22 + .../App/Config/ReinitableConfigInterface.php | 2 +- .../Framework/App/Config/Scope/Converter.php | 2 +- .../App/Config/Scope/ReaderInterface.php | 2 +- .../App/Config/Scope/ReaderPoolInterface.php | 17 - .../Framework/App/Config/Scope/Validator.php | 92 + .../App/Config/ScopeCodeResolver.php | 61 + .../App/Config/ScopeConfigInterface.php | 2 +- .../Framework/App/Config/ScopePool.php | 156 - .../App/Config/Spi/PostProcessorInterface.php | 27 + .../App/Config/Spi/PreProcessorInterface.php | 20 + .../Framework/App/Config/Storage/Writer.php | 2 +- .../App/Config/Storage/WriterInterface.php | 2 +- .../Magento/Framework/App/Config/Value.php | 2 +- .../Framework/App/Config/ValueFactory.php | 2 +- .../Framework/App/Config/ValueInterface.php | 2 +- .../Magento/Framework/App/Console/Request.php | 2 +- .../Framework/App/Console/Response.php | 2 +- lib/internal/Magento/Framework/App/Cron.php | 18 +- .../Framework/App/DefaultPath/DefaultPath.php | 2 +- .../Framework/App/DefaultPathInterface.php | 2 +- .../Framework/App/DeploymentConfig.php | 13 +- .../Framework/App/DeploymentConfig/Reader.php | 85 +- .../Framework/App/DeploymentConfig/Writer.php | 23 +- .../Writer/FormatterInterface.php | 5 +- .../DeploymentConfig/Writer/PhpFormatter.php | 23 +- .../Magento/Framework/App/DesignInterface.php | 2 +- .../Magento/Framework/App/DocRootLocator.php | 2 +- .../Framework/App/EnvironmentFactory.php | 2 +- .../Framework/App/EnvironmentInterface.php | 2 +- .../Magento/Framework/App/ErrorHandler.php | 2 +- .../App/Filesystem/DirectoryList.php | 33 +- .../Magento/Framework/App/FrontController.php | 2 +- .../App/FrontControllerInterface.php | 2 +- .../Framework/App/Helper/AbstractHelper.php | 2 +- .../Magento/Framework/App/Helper/Context.php | 2 +- lib/internal/Magento/Framework/App/Http.php | 5 +- .../Magento/Framework/App/Http/Context.php | 25 +- .../App/Interception/Cache/CompiledConfig.php | 2 +- .../Magento/Framework/App/Language/Config.php | 2 +- .../Framework/App/Language/ConfigFactory.php | 2 +- .../Framework/App/Language/Dictionary.php | 2 +- .../Framework/App/Language/package.xsd | 2 +- .../Magento/Framework/App/MaintenanceMode.php | 2 +- .../Framework/App/MutableScopeConfig.php | 39 +- .../Magento/Framework/App/ObjectManager.php | 2 +- .../App/ObjectManager/ConfigCache.php | 28 +- .../App/ObjectManager/ConfigLoader.php | 27 +- .../ObjectManager/ConfigLoader/Compiled.php | 31 +- .../Environment/AbstractEnvironment.php | 2 +- .../ObjectManager/Environment/Compiled.php | 2 +- .../ObjectManager/Environment/Developer.php | 2 +- .../Framework/App/ObjectManagerFactory.php | 24 +- .../Magento/Framework/App/PageCache/Cache.php | 2 +- .../Framework/App/PageCache/FormKey.php | 2 +- .../Framework/App/PageCache/Identifier.php | 2 +- .../Framework/App/PageCache/Kernel.php | 128 +- .../App/PageCache/NotCacheableInterface.php | 2 +- .../Framework/App/PageCache/Version.php | 2 +- .../App/PlainTextRequestInterface.php | 25 + .../Magento/Framework/App/ProductMetadata.php | 2 +- .../App/ProductMetadataInterface.php | 2 +- .../Framework/App/ReinitableConfig.php | 8 +- .../Framework/App/Request/DataPersistor.php | 2 +- .../App/Request/DataPersistorInterface.php | 2 +- .../Magento/Framework/App/Request/Http.php | 6 +- .../Request/PathInfoProcessorInterface.php | 2 +- .../Framework/App/RequestContentInterface.php | 18 + .../Magento/Framework/App/RequestFactory.php | 2 +- .../Framework/App/RequestInterface.php | 2 +- .../Framework/App/RequestSafetyInterface.php | 2 +- .../Framework/App/ResourceConnection.php | 2 +- .../App/ResourceConnection/Config.php | 57 +- .../ResourceConnection/Config/Converter.php | 2 +- .../App/ResourceConnection/Config/Reader.php | 2 +- .../Config/SchemaLocator.php | 2 +- .../ResourceConnection/ConfigInterface.php | 2 +- .../ConnectionAdapterInterface.php | 2 +- .../ResourceConnection/ConnectionFactory.php | 2 +- .../App/ResourceConnection/SourceFactory.php | 2 +- .../SourceProviderInterface.php | 2 +- .../Framework/App/Response/FileInterface.php | 2 +- .../Framework/App/Response/HeaderManager.php | 2 +- .../HeaderProvider/AbstractHeaderProvider.php | 2 +- .../HeaderProviderInterface.php | 2 +- .../HeaderProvider/XContentTypeOptions.php | 2 +- .../Response/HeaderProvider/XFrameOptions.php | 2 +- .../Response/HeaderProvider/XssProtection.php | 2 +- .../Magento/Framework/App/Response/Http.php | 2 +- .../App/Response/Http/FileFactory.php | 2 +- .../Framework/App/Response/HttpInterface.php | 2 +- .../App/Response/RedirectInterface.php | 2 +- .../Magento/Framework/App/ResponseFactory.php | 2 +- .../Framework/App/ResponseInterface.php | 2 +- .../Magento/Framework/App/Route/Config.php | 29 +- .../Framework/App/Route/Config/Converter.php | 2 +- .../Framework/App/Route/Config/Reader.php | 2 +- .../App/Route/Config/SchemaLocator.php | 2 +- .../Framework/App/Route/ConfigInterface.php | 2 +- .../App/Route/ConfigInterface/Proxy.php | 2 +- .../Framework/App/Router/ActionList.php | 27 +- .../Magento/Framework/App/Router/Base.php | 5 +- .../Framework/App/Router/DefaultRouter.php | 2 +- .../Framework/App/Router/NoRouteHandler.php | 2 +- .../App/Router/NoRouteHandlerInterface.php | 2 +- .../App/Router/NoRouteHandlerList.php | 2 +- .../App/Router/PathConfigInterface.php | 2 +- .../Magento/Framework/App/RouterInterface.php | 2 +- .../Magento/Framework/App/RouterList.php | 2 +- .../Framework/App/RouterListInterface.php | 2 +- .../App/Rss/DataProviderInterface.php | 2 +- .../Framework/App/Rss/RssManagerInterface.php | 2 +- .../Magento/Framework/App/Rss/UrlBuilder.php | 2 +- .../Framework/App/Rss/UrlBuilderInterface.php | 2 +- .../Magento/Framework/App/Scope/Source.php | 2 +- .../Magento/Framework/App/Scope/Validator.php | 90 + .../App/Scope/ValidatorInterface.php | 25 + .../App/ScopeFallbackResolverInterface.php | 2 +- .../Magento/Framework/App/ScopeInterface.php | 2 +- .../Framework/App/ScopeResolverInterface.php | 2 +- .../Framework/App/ScopeResolverPool.php | 4 +- .../App/ScopeTreeProviderInterface.php | 2 +- .../Framework/App/ScopeValidatorInterface.php | 2 +- .../Magento/Framework/App/SetupInfo.php | 10 +- lib/internal/Magento/Framework/App/Shell.php | 2 +- lib/internal/Magento/Framework/App/State.php | 2 +- .../Framework/App/State/CleanupFiles.php | 7 +- .../Magento/Framework/App/StaticResource.php | 2 +- .../Framework/App/TemplateTypesInterface.php | 2 +- .../App/Test/Unit/AclResourceTest.php | 2 +- .../Test/Unit/Action/AbstractActionTest.php | 4 +- .../App/Test/Unit/Action/ActionTest.php | 2 +- .../App/Test/Unit/Action/ForwardTest.php | 2 +- .../Test/Unit/Action/Plugin/DesignTest.php | 2 +- .../App/Test/Unit/Action/Stub/ActionStub.php | 2 +- .../App/Test/Unit/ActionFlagTest.php | 2 +- .../Framework/App/Test/Unit/AreaListTest.php | 2 +- .../Framework/App/Test/Unit/AreaTest.php | 2 +- .../Arguments/ArgumentInterpreterTest.php | 2 +- .../Arguments/FileResolver/PrimaryTest.php | 2 +- .../FileResolver/_files/app/etc/config.xml | 2 +- .../_files/app/etc/custom/config.xml | 2 +- .../_files/primary/app/etc/di.xml | 2 +- .../_files/primary/app/etc/some_config/di.xml | 2 +- .../Framework/App/Test/Unit/BootstrapTest.php | 2 +- .../Test/Unit/Cache/FlushCacheByTagsTest.php | 12 +- .../Test/Unit/Cache/Frontend/FactoryTest.php | 2 +- .../FactoryTest/CacheDecoratorDummy.php | 2 +- .../App/Test/Unit/Cache/Frontend/PoolTest.php | 2 +- .../App/Test/Unit/Cache/ManagerTest.php | 2 +- .../App/Test/Unit/Cache/StateTest.php | 2 +- .../App/Test/Unit/Cache/Tag/ResolverTest.php | 64 + .../Unit/Cache/Tag/Strategy/DummyTest.php | 36 + .../Unit/Cache/Tag/Strategy/FactoryTest.php | 86 + .../Cache/Tag/Strategy/IdentifierTest.php | 46 + .../Test/Unit/Cache/Type/AccessProxyTest.php | 2 +- .../App/Test/Unit/Cache/Type/ConfigTest.php | 2 +- .../Test/Unit/Cache/Type/FrontendPoolTest.php | 2 +- .../App/Test/Unit/Cache/Type/GenericTest.php | 2 +- .../App/Test/Unit/Cache/TypeListTest.php | 42 +- .../Framework/App/Test/Unit/CacheTest.php | 2 +- .../App/Test/Unit/Config/BaseFactoryTest.php | 2 +- .../Unit/Config/ConfigPathResolverTest.php | 67 + .../Config/ConfigSourceAggregatedTest.php | 69 + .../Unit/Config/Data/ProcessorFactoryTest.php | 2 +- .../App/Test/Unit/Config/DataFactoryTest.php | 2 +- .../App/Test/Unit/Config/DataTest.php | 2 +- .../App/Test/Unit/Config/ElementTest.php | 2 +- .../App/Test/Unit/Config/FileResolverTest.php | 2 +- .../Unit/Config/Initial/ConverterTest.php | 2 +- .../Test/Unit/Config/Initial/ReaderTest.php | 2 +- .../Unit/Config/Initial/SchemaLocatorTest.php | 2 +- .../App/Test/Unit/Config/Initial/XsdTest.php | 2 +- .../Unit/Config/Initial/_files/config.xml | 2 +- .../Unit/Config/Initial/_files/config.xsd | 4 +- .../Initial/_files/converted_config.php | 2 +- .../Config/Initial/_files/initial_config1.xml | 2 +- .../Config/Initial/_files/initial_config2.xml | 2 +- .../Initial/_files/initial_config_merged.php | 2 +- .../Initial/_files/invalidConfigXmlArray.php | 2 +- .../Config/Initial/_files/invalid_config.xml | 2 +- .../Config/Initial/_files/valid_config.xml | 2 +- .../Unit/Config/InitialConfigSourceTest.php | 54 + .../App/Test/Unit/Config/InitialTest.php | 78 +- .../MetadataConfigTypeProcessorTest.php | 79 + .../Unit/Config/MetadataProcessorTest.php | 80 +- .../Unit/Config/PreProcessorCompositeTest.php | 40 + .../Test/Unit/Config/Scope/ConverterTest.php | 2 +- .../Test/Unit/Config/Scope/ValidatorTest.php | 146 + .../Unit/Config/ScopeCodeResolverTest.php | 68 + .../App/Test/Unit/Config/ScopePoolTest.php | 168 - .../Test/Unit/Config/Storage/WriterTest.php | 2 +- .../App/Test/Unit/Config/ValueFactoryTest.php | 2 +- .../App/Test/Unit/Config/ValueTest.php | 2 +- .../App/Test/Unit/Config/XsdTest.php | 2 +- .../App/Test/Unit/Config/_files/element.xml | 2 +- .../Config/_files/invalidRoutesXmlArray.php | 2 +- .../Test/Unit/Config/_files/valid_routes.xml | 2 +- .../Framework/App/Test/Unit/ConfigTest.php | 83 + .../App/Test/Unit/Console/CommandListTest.php | 2 +- .../App/Test/Unit/Console/ResponseTest.php | 2 +- .../Framework/App/Test/Unit/CronTest.php | 30 +- .../Framework/App/Test/Unit/DefaultClass.php | 2 +- .../Test/Unit/DefaultPath/DefaultPathTest.php | 2 +- .../Test/Unit/DeploymentConfig/ReaderTest.php | 13 +- .../Writer/PhpFormatterTest.php | 127 +- .../Test/Unit/DeploymentConfig/WriterTest.php | 34 +- .../Unit/DeploymentConfig/_files/config.php | 2 +- .../Unit/DeploymentConfig/_files/custom.php | 2 +- .../_files/duplicateConfig.php | 2 +- .../Test/Unit/DeploymentConfig/_files/env.php | 2 +- .../Unit/DeploymentConfig/_files/mergeOne.php | 2 +- .../Unit/DeploymentConfig/_files/mergeTwo.php | 2 +- .../App/Test/Unit/DeploymentConfigTest.php | 2 +- .../App/Test/Unit/DocRootLocatorTest.php | 2 +- .../App/Test/Unit/ErrorHandlerTest.php | 2 +- .../Unit/Filesystem/DirectoryListTest.php | 29 +- .../Framework/App/Test/Unit/FrontClass.php | 2 +- .../App/Test/Unit/FrontControllerTest.php | 2 +- .../App/Test/Unit/Http/ContextTest.php | 17 +- .../Framework/App/Test/Unit/HttpTest.php | 2 +- .../App/Test/Unit/Language/ConfigTest.php | 2 +- .../App/Test/Unit/Language/DictionaryTest.php | 2 +- .../Test/Unit/Language/_files/language.xml | 2 +- .../App/Test/Unit/MaintenanceModeTest.php | 2 +- .../Unit/ObjectManager/ConfigCacheTest.php | 63 +- .../Unit/ObjectManager/ConfigLoaderTest.php | 97 +- .../Environment/CompiledTest.php | 2 +- .../Environment/CompiledTesting.php | 2 +- .../Environment/ConfigTesting.php | 2 +- .../Environment/DeveloperTest.php | 2 +- .../Test/Unit/ObjectManager/FactoryStub.php | 2 +- .../Test/Unit/ObjectManagerFactoryTest.php | 2 +- .../App/Test/Unit/PageCache/FormKeyTest.php | 2 +- .../Test/Unit/PageCache/IdentifierTest.php | 2 +- .../App/Test/Unit/PageCache/KernelTest.php | 156 +- .../App/Test/Unit/PageCache/PageCacheTest.php | 2 +- .../App/Test/Unit/PageCache/VersionTest.php | 2 +- .../App/Test/Unit/ProductMetadataTest.php | 2 +- .../App/Test/Unit/ReinitableConfigTest.php | 22 - .../App/Test/Unit/Request/HttpTest.php | 2 +- .../App/Test/Unit/RequestFactoryTest.php | 2 +- .../Config/ConverterTest.php | 2 +- .../ResourceConnection/Config/ReaderTest.php | 2 +- .../Config/SchemaLocatorTest.php | 2 +- .../ResourceConnection/Config/XsdTest.php | 2 +- .../_files/invalidResourcesXmlArray.php | 2 +- .../Config/_files/resources.php | 2 +- .../Config/_files/resources.xml | 2 +- .../Config/_files/valid_resources.xml | 2 +- .../Unit/ResourceConnection/ConfigTest.php | 121 +- .../ConnectionFactoryTest.php | 2 +- .../HeaderProvider/XFrameOptionsTest.php | 2 +- .../HeaderProvider/XssProtectionTest.php | 2 +- .../Unit/Response/Http/FileFactoryTest.php | 2 +- .../App/Test/Unit/Response/HttpTest.php | 2 +- .../App/Test/Unit/ResponseFactoryTest.php | 2 +- .../Test/Unit/Route/Config/ConverterTest.php | 2 +- .../Unit/Route/Config/SchemaLocatorTest.php | 2 +- .../Test/Unit/Route/Config/_files/routes.php | 2 +- .../Test/Unit/Route/Config/_files/routes.xml | 2 +- .../Unit/Route/ConfigInterface/ProxyTest.php | 2 +- .../App/Test/Unit/Route/ConfigTest.php | 143 +- .../App/Test/Unit/Router/ActionListTest.php | 102 +- .../App/Test/Unit/Router/BaseTest.php | 12 +- .../Test/Unit/Router/DefaultRouterTest.php | 2 +- .../Unit/Router/NoRouteHandlerListTest.php | 2 +- .../Test/Unit/Router/NoRouteHandlerTest.php | 2 +- .../App/Test/Unit/RouterListTest.php | 2 +- .../App/Test/Unit/Scope/ValidatorTest.php | 141 + .../App/Test/Unit/ScopeResolverPoolTest.php | 2 +- .../Framework/App/Test/Unit/SetupInfoTest.php | 9 +- .../Framework/App/Test/Unit/ShellTest.php | 2 +- .../App/Test/Unit/State/CleanupFilesTest.php | 6 +- .../Framework/App/Test/Unit/StateTest.php | 2 +- .../App/Test/Unit/StaticResourceTest.php | 2 +- .../Unit/Utility/AggregateInvokerTest.php | 2 +- .../App/Test/Unit/Utility/FilesTest.php | 2 +- .../MaterializationStrategy/CopyTest.php | 2 +- .../MaterializationStrategy/FactoryTest.php | 2 +- .../MaterializationStrategy/SymlinkTest.php | 2 +- .../Test/Unit/View/Asset/PublisherTest.php | 2 +- .../Deployment/Version/Storage/FileTest.php | 47 +- .../Test/Unit/View/Deployment/VersionTest.php | 95 +- .../Framework/App/Test/Unit/ViewTest.php | 2 +- .../App/Test/Unit/_files/app/etc/di.xml | 2 +- .../Framework/App/Test/Unit/_files/config.php | 2 +- .../Unit/_files/other/local_developer.php | 2 +- .../_files/other/local_developer_merged.php | 2 +- .../App/Test/Unit/_files/pub/index.php | 5 + .../App/Test/Unit/_files/setup/index.php | 5 + .../App/Utility/AggregateInvoker.php | 2 +- .../Magento/Framework/App/Utility/Classes.php | 2 +- .../Magento/Framework/App/Utility/Files.php | 2 +- lib/internal/Magento/Framework/App/View.php | 2 +- .../Asset/MaterializationStrategy/Copy.php | 2 +- .../Asset/MaterializationStrategy/Factory.php | 2 +- .../StrategyInterface.php | 2 +- .../Asset/MaterializationStrategy/Symlink.php | 2 +- .../Framework/App/View/Asset/Publisher.php | 2 +- .../Framework/App/View/Deployment/Version.php | 58 +- .../View/Deployment/Version/Storage/File.php | 11 +- .../Deployment/Version/StorageInterface.php | 2 +- .../Magento/Framework/App/ViewInterface.php | 2 +- .../Magento/Framework/App/etc/resources.xsd | 2 +- .../Magento/Framework/App/etc/routes.xsd | 2 +- .../Framework/App/etc/routes_merged.xsd | 2 +- .../Magento/Framework/AppInterface.php | 2 +- lib/internal/Magento/Framework/Archive.php | 2 +- .../Framework/Archive/AbstractArchive.php | 2 +- .../Framework/Archive/ArchiveInterface.php | 2 +- lib/internal/Magento/Framework/Archive/Bz.php | 2 +- lib/internal/Magento/Framework/Archive/Gz.php | 2 +- .../Magento/Framework/Archive/Helper/File.php | 2 +- .../Framework/Archive/Helper/File/Bz.php | 2 +- .../Framework/Archive/Helper/File/Gz.php | 2 +- .../Magento/Framework/Archive/Tar.php | 4 +- .../Framework/Archive/Test/Unit/ZipTest.php | 2 +- .../Magento/Framework/Archive/Zip.php | 2 +- .../Magento/Framework/Authorization.php | 2 +- .../Framework/Authorization/Factory.php | 2 +- .../Framework/Authorization/Policy/Acl.php | 2 +- .../Authorization/Policy/DefaultPolicy.php | 2 +- .../Authorization/PolicyInterface.php | 2 +- .../RoleLocator/DefaultRoleLocator.php | 2 +- .../Authorization/RoleLocatorInterface.php | 2 +- .../Test/Unit/Policy/AclTest.php | 2 +- .../Test/Unit/Policy/DefaultTest.php | 2 +- .../Framework/AuthorizationInterface.php | 2 +- .../Autoload/AutoloaderInterface.php | 2 +- .../Framework/Autoload/AutoloaderRegistry.php | 2 +- .../Framework/Autoload/ClassLoaderWrapper.php | 2 +- .../Magento/Framework/Autoload/ClassMap.php | 2 +- .../Magento/Framework/Autoload/Populator.php | 4 +- .../Test/Unit/ClassLoaderWrapperTest.php | 2 +- .../Autoload/Test/Unit/PopulatorTest.php | 6 +- .../Framework/Backup/AbstractBackup.php | 2 +- .../Magento/Framework/Backup/Archive/Tar.php | 2 +- .../Framework/Backup/BackupException.php | 2 +- .../Framework/Backup/BackupInterface.php | 2 +- lib/internal/Magento/Framework/Backup/Db.php | 2 +- .../Framework/Backup/Db/BackupDbInterface.php | 2 +- .../Framework/Backup/Db/BackupFactory.php | 2 +- .../Framework/Backup/Db/BackupInterface.php | 2 +- .../Backup/Exception/CantLoadSnapshot.php | 2 +- .../Backup/Exception/FtpConnectionFailed.php | 2 +- .../Backup/Exception/FtpValidationFailed.php | 2 +- .../Backup/Exception/NotEnoughFreeSpace.php | 2 +- .../Backup/Exception/NotEnoughPermissions.php | 2 +- .../Magento/Framework/Backup/Factory.php | 2 +- .../Magento/Framework/Backup/Filesystem.php | 6 +- .../Framework/Backup/Filesystem/Helper.php | 2 +- .../Backup/Filesystem/Iterator/File.php | 2 +- .../Backup/Filesystem/Iterator/Filter.php | 2 +- .../Filesystem/Rollback/AbstractRollback.php | 2 +- .../Backup/Filesystem/Rollback/Fs.php | 2 +- .../Backup/Filesystem/Rollback/Ftp.php | 2 +- .../Magento/Framework/Backup/Media.php | 2 +- .../Magento/Framework/Backup/Nomedia.php | 2 +- .../Magento/Framework/Backup/Snapshot.php | 2 +- .../Backup/Test/Unit/FactoryTest.php | 2 +- .../Backup/Test/Unit/Filesystem/Helper.php | 2 +- .../Test/Unit/Filesystem/Rollback/Fs.php | 2 +- .../Test/Unit/Filesystem/Rollback/FsTest.php | 2 +- .../Test/Unit/Filesystem/Rollback/Ftp.php | 2 +- .../Filesystem/Rollback/_files/ioMock.php | 2 +- .../Backup/Test/Unit/FilesystemTest.php | 2 +- .../Framework/Backup/Test/Unit/MediaTest.php | 2 +- .../Backup/Test/Unit/NomediaTest.php | 2 +- .../Backup/Test/Unit/SnapshotTest.php | 2 +- .../Backup/Test/Unit/_files/app_dirs.php | 2 +- .../Test/Unit/_files/app_dirs_rollback.php | 2 +- .../Framework/Backup/Test/Unit/_files/io.php | 2 +- .../Framework/Cache/Backend/Database.php | 2 +- .../Backend/Decorator/AbstractDecorator.php | 2 +- .../Cache/Backend/Decorator/Compression.php | 2 +- .../Framework/Cache/Backend/Eaccelerator.php | 2 +- .../Framework/Cache/Backend/Memcached.php | 2 +- .../Framework/Cache/Backend/MongoDb.php | 2 +- .../Magento/Framework/Cache/Config.php | 2 +- .../Framework/Cache/Config/Converter.php | 2 +- .../Magento/Framework/Cache/Config/Data.php | 17 +- .../Magento/Framework/Cache/Config/Reader.php | 2 +- .../Framework/Cache/Config/SchemaLocator.php | 2 +- .../Framework/Cache/ConfigInterface.php | 2 +- lib/internal/Magento/Framework/Cache/Core.php | 2 +- .../Framework/Cache/Frontend/Adapter/Zend.php | 2 +- .../Cache/Frontend/Decorator/Bare.php | 2 +- .../Cache/Frontend/Decorator/Logger.php | 2 +- .../Cache/Frontend/Decorator/Profiler.php | 2 +- .../Cache/Frontend/Decorator/TagScope.php | 2 +- .../Framework/Cache/FrontendInterface.php | 2 +- .../Framework/Cache/InvalidateLogger.php | 2 +- .../Cache/Test/Unit/Backend/DatabaseTest.php | 2 +- .../Backend/Decorator/CompressionTest.php | 2 +- .../Decorator/DecoratorAbstractTest.php | 2 +- .../Cache/Test/Unit/Backend/MongoDbTest.php | 2 +- .../Test/Unit/Backend/_files/MongoBinData.txt | 2 +- .../Cache/Test/Unit/Config/ConverterTest.php | 2 +- .../Test/Unit/Config/SchemaLocatorTest.php | 2 +- .../Test/Unit/Config/_files/cache_config.php | 2 +- .../Test/Unit/Config/_files/cache_config.xml | 2 +- .../Framework/Cache/Test/Unit/ConfigTest.php | 2 +- .../Framework/Cache/Test/Unit/CoreTest.php | 2 +- .../Cache/Test/Unit/CoreTestMock.php | 2 +- .../Test/Unit/Frontend/Adapter/ZendTest.php | 2 +- .../Test/Unit/Frontend/Decorator/BareTest.php | 2 +- .../Unit/Frontend/Decorator/ProfilerTest.php | 2 +- .../Unit/Frontend/Decorator/TagScopeTest.php | 2 +- .../Cache/Test/Unit/InvalidateLoggerTest.php | 2 +- .../Magento/Framework/Cache/etc/cache.xsd | 2 +- .../Magento/Framework/Code/GeneratedFiles.php | 20 +- .../Magento/Framework/Code/Generator.php | 2 +- .../Framework/Code/Generator/Autoloader.php | 2 +- .../Code/Generator/ClassGenerator.php | 6 +- .../Code/Generator/CodeGeneratorInterface.php | 2 +- .../Code/Generator/DefinedClasses.php | 2 +- .../Code/Generator/EntityAbstract.php | 2 +- .../Code/Generator/InterfaceGenerator.php | 2 +- .../Generator/InterfaceMethodGenerator.php | 2 +- .../Magento/Framework/Code/Generator/Io.php | 8 +- .../Code/Minifier/Adapter/Css/CSSmin.php | 2 +- .../Code/Minifier/Adapter/Js/JShrink.php | 2 +- .../Code/Minifier/AdapterInterface.php | 2 +- .../Magento/Framework/Code/NameBuilder.php | 2 +- .../Framework/Code/Reader/ArgumentsReader.php | 4 +- .../Framework/Code/Reader/ClassReader.php | 2 +- .../Code/Reader/ClassReaderInterface.php | 2 +- .../Code/Reader/SourceArgumentsReader.php | 2 +- .../Code/Test/Unit/GeneratedFilesTest.php | 10 +- .../Unit/Generator/ClassGeneratorTest.php | 30 +- .../Unit/Generator/DefinedClassesTest.php | 2 +- .../Unit/Generator/EntityAbstractTest.php | 2 +- .../Unit/Generator/InterfaceGeneratorTest.php | 2 +- .../Code/Test/Unit/Generator/IoTest.php | 2 +- .../Unit/Generator/TestAsset/ParentClass.php | 5 +- .../Unit/Generator/TestAsset/SourceClass.php | 5 +- .../TestAsset/TestGenerationClass.php | 2 +- .../Code/Test/Unit/GeneratorTest.php | 2 +- .../Unit/Minifier/Adapter/Css/CssMinTest.php | 2 +- .../Unit/Minifier/Adapter/Js/JShrinkTest.php | 2 +- .../Test/Unit/Minifier/_files/js/original.js | 2 +- .../Validator/NotProtectedExtensionTest.php | 2 +- .../Code/Test/Unit/NameBuilderTest.php | 2 +- .../Test/Unit/Reader/ArgumentsReaderTest.php | 2 +- .../_files/ClassesForArgumentsReader.php | 2 +- .../Unit/Validator/ArgumentSequenceTest.php | 2 +- .../ConstructorArgumentTypesTest.php | 2 +- .../Validator/ConstructorIntegrityTest.php | 2 +- .../Unit/Validator/ContextAggregationTest.php | 2 +- .../Unit/Validator/TypeDuplicationTest.php | 2 +- .../_files/ClassesForArgumentSequence.php | 2 +- .../_files/ClassesForConstructorIntegrity.php | 2 +- .../_files/ClassesForContextAggregation.php | 2 +- .../_files/ClassesForTypeDuplication.php | 2 +- .../Code/Test/Unit/ValidatorTest.php | 2 +- .../SomeModule/Model/ElementFactory.php | 2 +- .../Magento/SomeModule/Model/Five/Test.php | 2 +- .../Magento/SomeModule/Model/Four/Test.php | 2 +- .../Magento/SomeModule/Model/One/Test.php | 2 +- .../code/Magento/SomeModule/Model/Proxy.php | 2 +- .../SomeModule/Model/SevenInterface.php | 2 +- .../Magento/SomeModule/Model/Six/Test.php | 2 +- .../Magento/SomeModule/Model/Three/Test.php | 2 +- .../Magento/SomeModule/Model/Two/Test.php | 2 +- .../Magento/Framework/Code/Validator.php | 2 +- .../Code/Validator/ArgumentSequence.php | 2 +- .../Validator/ConstructorArgumentTypes.php | 2 +- .../Code/Validator/ConstructorIntegrity.php | 2 +- .../Code/Validator/ContextAggregation.php | 2 +- .../Code/Validator/TypeDuplication.php | 2 +- .../Framework/Code/ValidatorInterface.php | 2 +- .../Framework/Communication/Config.php | 2 +- .../Communication/Config/CompositeReader.php | 2 +- .../Communication/Config/ConfigParser.php | 4 +- .../Framework/Communication/Config/Data.php | 16 +- .../Communication/Config/Reader/EnvReader.php | 2 +- .../Config/Reader/EnvReader/Validator.php | 2 +- .../Communication/Config/Reader/XmlReader.php | 2 +- .../Config/Reader/XmlReader/Converter.php | 4 +- .../Config/Reader/XmlReader/SchemaLocator.php | 2 +- .../Config/Reader/XmlReader/Validator.php | 2 +- .../Config/ReflectionGenerator.php | 22 +- .../Communication/Config/Validator.php | 2 +- .../Communication/ConfigInterface.php | 2 +- .../Communication/etc/communication.xsd | 8 +- .../Framework/Component/ComponentFile.php | 2 +- .../Component/ComponentRegistrar.php | 2 +- .../Component/ComponentRegistrarInterface.php | 2 +- .../Magento/Framework/Component/DirSearch.php | 2 +- .../Component/Test/Unit/ComponentFileTest.php | 2 +- .../Test/Unit/ComponentRegistrarTest.php | 2 +- .../Component/Test/Unit/DirSearchTest.php | 2 +- .../Framework/Composer/BufferIoFactory.php | 2 +- .../Framework/Composer/ComposerFactory.php | 2 +- .../Composer/ComposerInformation.php | 2 +- .../Framework/Composer/ComposerJsonFinder.php | 2 +- .../Framework/Composer/DependencyChecker.php | 2 +- .../Framework/Composer/MagentoComponent.php | 2 +- .../MagentoComposerApplicationFactory.php | 2 +- .../Magento/Framework/Composer/Remove.php | 2 +- .../Test/Unit/ComposerFactoryTest.php | 2 +- .../Test/Unit/ComposerInformationTest.php | 2 +- .../Test/Unit/DependencyCheckerTest.php | 2 +- .../Magento/Framework/Config/AbstractXml.php | 2 +- .../Framework/Config/CacheInterface.php | 2 +- .../Framework/Config/Composer/Package.php | 2 +- .../Config/ConfigOptionsListConstants.php | 12 +- .../Magento/Framework/Config/Converter.php | 7 +- .../Framework/Config/Converter/Dom.php | 2 +- .../Framework/Config/Converter/Dom/Flat.php | 2 +- .../Framework/Config/ConverterInterface.php | 2 +- .../Magento/Framework/Config/Data.php | 30 +- .../Framework/Config/Data/ConfigData.php | 2 +- .../Magento/Framework/Config/Data/Scoped.php | 25 +- .../Framework/Config/DataInterface.php | 2 +- .../Config/DesignResolverInterface.php | 2 +- lib/internal/Magento/Framework/Config/Dom.php | 2 +- .../Framework/Config/Dom/ArrayNodeConfig.php | 2 +- .../Config/Dom/NodeMergingConfig.php | 2 +- .../Framework/Config/Dom/NodePathMatcher.php | 2 +- .../Framework/Config/Dom/UrnResolver.php | 2 +- .../Config/Dom/ValidationException.php | 2 +- .../Config/Dom/ValidationSchemaException.php | 2 +- .../Magento/Framework/Config/DomFactory.php | 2 +- .../Framework/Config/File/ConfigFilePool.php | 44 +- .../Magento/Framework/Config/FileIterator.php | 2 +- .../Framework/Config/FileIteratorFactory.php | 2 +- .../Magento/Framework/Config/FileResolver.php | 10 +- .../Config/FileResolverInterface.php | 2 +- .../Framework/Config/GenericSchemaLocator.php | 2 +- .../Magento/Framework/Config/Reader.php | 75 + .../Framework/Config/Reader/Filesystem.php | 2 +- .../Framework/Config/ReaderInterface.php | 2 +- .../Framework/Config/SchemaLocator.php | 2 +- .../Config/SchemaLocatorInterface.php | 2 +- .../Magento/Framework/Config/Scope.php | 2 +- .../Framework/Config/ScopeInterface.php | 2 +- .../Framework/Config/ScopeListInterface.php | 2 +- .../Config/Test/Unit/Composer/PackageTest.php | 2 +- .../Test/Unit/Converter/Dom/FlatTest.php | 2 +- .../Config/Test/Unit/Converter/DomTest.php | 2 +- .../Config/Test/Unit/Data/ConfigDataTest.php | 2 +- .../Config/Test/Unit/Data/ScopedTest.php | 66 +- .../Framework/Config/Test/Unit/DataTest.php | 101 +- .../Test/Unit/Dom/ArrayNodeConfigTest.php | 2 +- .../Test/Unit/Dom/NodeMergingConfigTest.php | 2 +- .../Test/Unit/Dom/NodePathMatcherTest.php | 2 +- .../Config/Test/Unit/Dom/UrnResolverTest.php | 2 +- .../Framework/Config/Test/Unit/DomTest.php | 2 +- .../Test/Unit/File/ConfigFilePoolTest.php | 2 +- .../Config/Test/Unit/FileIteratorTest.php | 2 +- .../Test/Unit/GenericSchemaLocatorTest.php | 2 +- .../Test/Unit/Reader/FilesystemTest.php | 2 +- .../Framework/Config/Test/Unit/ReaderTest.php | 46 + .../Framework/Config/Test/Unit/ScopeTest.php | 2 +- .../Framework/Config/Test/Unit/ThemeTest.php | 2 +- .../Config/Test/Unit/ValidationStateTest.php | 2 +- .../Config/Test/Unit/ViewFactoryTest.php | 2 +- .../Framework/Config/Test/Unit/XsdTest.php | 2 +- .../_files/area/default_default/theme.xml | 2 +- .../Unit/_files/area/default_test/theme.xml | 2 +- .../Unit/_files/area/default_test2/theme.xml | 2 +- .../Unit/_files/area/test_default/theme.xml | 2 +- .../theme.xml | 2 +- .../Unit/_files/converter/dom/attributes.php | 2 +- .../Unit/_files/converter/dom/attributes.xml | 2 +- .../Test/Unit/_files/converter/dom/cdata.php | 2 +- .../Test/Unit/_files/converter/dom/cdata.xml | 2 +- .../Unit/_files/converter/dom/flat/result.php | 2 +- .../Unit/_files/converter/dom/flat/source.xml | 2 +- .../converter/dom/flat/source_notuniq.xml | 2 +- .../converter/dom/flat/source_wrongarray.xml | 2 +- .../Test/Unit/_files/dom/ambiguous_merged.xml | 2 +- .../Unit/_files/dom/ambiguous_new_one.xml | 2 +- .../Unit/_files/dom/ambiguous_new_two.xml | 2 +- .../Test/Unit/_files/dom/ambiguous_one.xml | 2 +- .../Test/Unit/_files/dom/ambiguous_two.xml | 2 +- .../Test/Unit/_files/dom/attributes.xml | 2 +- .../Unit/_files/dom/attributes_merged.xml | 2 +- .../Test/Unit/_files/dom/attributes_new.xml | 2 +- .../Test/Unit/_files/dom/converter/cdata.php | 2 +- .../Test/Unit/_files/dom/converter/cdata.xml | 2 +- .../_files/dom/converter/no_attributes.php | 2 +- .../_files/dom/converter/no_attributes.xml | 2 +- .../_files/dom/converter/with_attributes.php | 2 +- .../_files/dom/converter/with_attributes.xml | 2 +- .../Config/Test/Unit/_files/dom/ids.xml | 2 +- .../Test/Unit/_files/dom/ids_merged.xml | 2 +- .../Config/Test/Unit/_files/dom/ids_new.xml | 2 +- .../Test/Unit/_files/dom/namespaced.xml | 2 +- .../Unit/_files/dom/namespaced_merged.xml | 2 +- .../Test/Unit/_files/dom/namespaced_new.xml | 2 +- .../Config/Test/Unit/_files/dom/no_ids.xml | 2 +- .../Test/Unit/_files/dom/no_ids_merged.xml | 2 +- .../Test/Unit/_files/dom/no_ids_new.xml | 2 +- .../Test/Unit/_files/dom/override_node.xml | 4 +- .../Unit/_files/dom/override_node_merged.xml | 4 +- .../Unit/_files/dom/override_node_new.xml | 4 +- .../Config/Test/Unit/_files/dom/recursive.xml | 2 +- .../Test/Unit/_files/dom/recursive_deep.xml | 2 +- .../Unit/_files/dom/recursive_deep_merged.xml | 2 +- .../Unit/_files/dom/recursive_deep_new.xml | 2 +- .../Test/Unit/_files/dom/recursive_merged.xml | 2 +- .../Test/Unit/_files/dom/recursive_new.xml | 2 +- .../Config/Test/Unit/_files/dom/text_node.xml | 4 +- .../Test/Unit/_files/dom/text_node_merged.xml | 4 +- .../Test/Unit/_files/dom/text_node_new.xml | 4 +- .../Config/Test/Unit/_files/dom/types.xml | 2 +- .../Test/Unit/_files/dom/types_merged.xml | 2 +- .../Config/Test/Unit/_files/dom/types_new.xml | 2 +- .../Config/Test/Unit/_files/reader/config.xml | 4 +- .../Config/Test/Unit/_files/reader/schema.xsd | 4 +- .../Config/Test/Unit/_files/sample.xsd | 2 +- .../Config/Test/Unit/_files/theme_invalid.xml | 2 +- .../Config/Test/Unit/_files/view_invalid.xml | 2 +- .../Config/Test/Unit/_files/view_one.xml | 2 +- .../Config/Test/Unit/_files/view_two.xml | 2 +- .../Magento/Framework/Config/Theme.php | 2 +- .../Config/ValidationStateInterface.php | 2 +- .../Magento/Framework/Config/View.php | 2 +- .../Magento/Framework/Config/ViewFactory.php | 2 +- .../Magento/Framework/Config/etc/theme.xsd | 2 +- .../Magento/Framework/Config/etc/view.xsd | 2 +- .../Magento/Framework/Console/Cli.php | 241 +- .../Magento/Framework/Console/CommandList.php | 2 +- .../Console/CommandListInterface.php | 2 +- .../Framework/Console/CommandLocator.php | 2 +- .../Console/GenerationDirectoryAccess.php | 8 +- .../Framework/Controller/AbstractResult.php | 2 +- .../Framework/Controller/Index/Index.php | 2 +- .../Framework/Controller/Noroute/Index.php | 2 +- .../Framework/Controller/Result/Forward.php | 2 +- .../Framework/Controller/Result/Json.php | 2 +- .../Controller/Result/JsonFactory.php | 2 +- .../Framework/Controller/Result/Raw.php | 2 +- .../Framework/Controller/Result/Redirect.php | 2 +- .../Controller/Result/RedirectFactory.php | 2 +- .../Framework/Controller/ResultFactory.php | 2 +- .../Framework/Controller/ResultInterface.php | 2 +- .../Controller/Router/Route/Factory.php | 2 +- .../Test/Unit/Controller/Index/IndexTest.php | 2 +- .../Test/Unit/Controller/NorouteTest.php | 2 +- .../Test/Unit/Result/ForwardTest.php | 2 +- .../Controller/Test/Unit/Result/JsonTest.php | 2 +- .../Controller/Test/Unit/Result/RawTest.php | 2 +- .../Test/Unit/Result/RedirectFactoryTest.php | 2 +- .../Test/Unit/Result/RedirectTest.php | 2 +- .../Test/Unit/Router/Route/FactoryTest.php | 2 +- .../Framework/Convert/ConvertArray.php | 2 +- .../Magento/Framework/Convert/DataObject.php | 2 +- .../Magento/Framework/Convert/DataSize.php | 2 +- .../Magento/Framework/Convert/Excel.php | 2 +- .../Framework/Convert/ExcelFactory.php | 2 +- .../Convert/Test/Unit/ConvertArrayTest.php | 2 +- .../Convert/Test/Unit/DataSizeTest.php | 2 +- .../Convert/Test/Unit/ExcelFactoryTest.php | 2 +- .../Framework/Convert/Test/Unit/ExcelTest.php | 2 +- .../Convert/Test/Unit/ObjectTest.php | 2 +- .../Framework/Convert/Test/Unit/XmlTest.php | 2 +- .../Convert/Test/Unit/_files/sample.xml | 2 +- .../Magento/Framework/Convert/Xml.php | 2 +- .../Framework/Crontab/CrontabManager.php | 199 + .../Crontab/CrontabManagerInterface.php | 43 + .../Magento/Framework/Crontab/README.md | 12 + .../Framework/Crontab/TasksProvider.php | 33 + .../Crontab/TasksProviderInterface.php | 16 + .../Crontab/Test/Unit/CrontabManagerTest.php | 333 + .../Crontab/Test/Unit/TasksProviderTest.php | 34 + .../PreProcessor/Adapter/Less/Processor.php | 2 +- .../Framework/Css/PreProcessor/Config.php | 2 +- .../Css/PreProcessor/ErrorHandler.php | 2 +- .../PreProcessor/ErrorHandlerInterface.php | 2 +- .../File/Collector/Aggregated.php | 2 +- .../PreProcessor/File/Collector/Library.php | 2 +- .../PreProcessor/File/FileList/Collator.php | 2 +- .../Css/PreProcessor/File/Temporary.php | 2 +- .../FileGenerator/RelatedGenerator.php | 2 +- .../Css/PreProcessor/Instruction/Import.php | 15 +- .../Instruction/MagentoImport.php | 2 +- .../Adapter/Less/ProcessorTest.php | 2 +- .../PreProcessor/Adapter/Less/_file/test.css | 2 +- .../PreProcessor/Adapter/Less/_file/test.less | 4 +- .../File/Collector/AggregatedTest.php | 2 +- .../File/Collector/LibraryTest.php | 2 +- .../File/FileList/CollatorTest.php | 2 +- .../PreProcessor/Instruction/ImportTest.php | 68 +- .../Instruction/MagentoImportTest.php | 2 +- .../Unit/PreProcessor/_files/invalid.less | 2 +- .../Test/Unit/PreProcessor/_files/valid.less | 2 +- lib/internal/Magento/Framework/Currency.php | 2 +- .../Magento/Framework/CurrencyFactory.php | 2 +- .../Magento/Framework/CurrencyInterface.php | 2 +- .../Magento/Framework/DB/AbstractMapper.php | 2 +- .../Framework/DB/Adapter/AdapterInterface.php | 2 +- .../DB/Adapter/ConnectionException.php | 2 +- .../Magento/Framework/DB/Adapter/DdlCache.php | 2 +- .../DB/Adapter/DeadlockException.php | 2 +- .../DB/Adapter/DuplicateException.php | 13 + .../DB/Adapter/LockWaitException.php | 2 +- .../Framework/DB/Adapter/Pdo/Mysql.php | 26 +- .../DataConverter/DataConversionException.php | 16 + .../DataConverter/DataConverterInterface.php | 22 + .../DB/DataConverter/SerializedToJson.php | 110 + .../Magento/Framework/DB/Ddl/Sequence.php | 2 +- .../Magento/Framework/DB/Ddl/Table.php | 2 +- .../Magento/Framework/DB/Ddl/Trigger.php | 2 +- .../Framework/DB/Ddl/TriggerFactory.php | 2 +- .../Framework/DB/ExpressionConverter.php | 2 +- .../DB/FieldDataConversionException.php | 21 + .../Framework/DB/FieldDataConverter.php | 105 + .../DB/FieldDataConverterFactory.php | 46 + .../Magento/Framework/DB/GenericMapper.php | 2 +- lib/internal/Magento/Framework/DB/Helper.php | 2 +- .../Framework/DB/Helper/AbstractHelper.php | 2 +- .../Framework/DB/Helper/Mysql/Fulltext.php | 2 +- .../Magento/Framework/DB/Logger/File.php | 2 +- .../Framework/DB/Logger/LoggerAbstract.php | 2 +- .../Magento/Framework/DB/Logger/Quiet.php | 2 +- .../Magento/Framework/DB/LoggerInterface.php | 2 +- .../Magento/Framework/DB/MapperFactory.php | 2 +- .../Magento/Framework/DB/MapperInterface.php | 2 +- .../Magento/Framework/DB/Platform/Quote.php | 2 +- .../Magento/Framework/DB/Profiler.php | 2 +- lib/internal/Magento/Framework/DB/Query.php | 2 +- .../Framework/DB/Query/BatchIterator.php | 4 +- .../DB/Query/BatchIteratorFactory.php | 4 +- .../DB/Query/BatchIteratorInterface.php | 68 + .../Framework/DB/Query/BatchRangeIterator.php | 209 + .../Magento/Framework/DB/Query/Generator.php | 122 +- .../Magento/Framework/DB/QueryBuilder.php | 2 +- .../Magento/Framework/DB/QueryFactory.php | 2 +- .../Magento/Framework/DB/QueryInterface.php | 2 +- lib/internal/Magento/Framework/DB/Select.php | 2 +- .../Framework/DB/Select/ColumnsRenderer.php | 2 +- .../Framework/DB/Select/DistinctRenderer.php | 2 +- .../Framework/DB/Select/ForUpdateRenderer.php | 2 +- .../Framework/DB/Select/FromRenderer.php | 2 +- .../Framework/DB/Select/GroupRenderer.php | 2 +- .../Framework/DB/Select/HavingRenderer.php | 2 +- .../Framework/DB/Select/InQueryModifier.php | 40 + .../Framework/DB/Select/LimitRenderer.php | 2 +- .../Framework/DB/Select/OrderRenderer.php | 2 +- .../DB/Select/QueryModifierFactory.php | 60 + .../DB/Select/QueryModifierInterface.php | 22 + .../Framework/DB/Select/RendererInterface.php | 2 +- .../Framework/DB/Select/RendererProxy.php | 2 +- .../Framework/DB/Select/SelectRenderer.php | 2 +- .../Framework/DB/Select/UnionRenderer.php | 2 +- .../Framework/DB/Select/WhereRenderer.php | 2 +- .../Magento/Framework/DB/SelectFactory.php | 2 +- .../DB/Sequence/SequenceInterface.php | 2 +- .../DB/Sql/ColumnValueExpression.php | 19 + .../Framework/DB/Sql/ConcatExpression.php | 2 +- .../Framework/DB/Sql/LimitExpression.php | 2 +- .../Framework/DB/Sql/LookupExpression.php | 2 +- .../Framework/DB/Sql/UnionExpression.php | 2 +- .../Framework/DB/Statement/Parameter.php | 2 +- .../Framework/DB/Statement/Pdo/Mysql.php | 2 +- .../Magento/Framework/DB/SubSelect.php | 2 +- .../Framework/DB/TemporaryTableService.php | 164 + .../DB/Test/Unit/AbstractMapperTest.php | 2 +- .../DB/Test/Unit/Adapter/Pdo/MysqlTest.php | 2 +- .../DataConverter/SerializedToJsonTest.php | 59 + .../DB/Test/Unit/Ddl/TriggerTest.php | 2 +- .../DB/Test/Unit/ExpressionConverterTest.php | 2 +- .../Unit/FieldDataConverterFactoryTest.php | 66 + .../DB/Test/Unit/FieldDataConverterTest.php | 151 + .../DB/Test/Unit/GenericMapperTest.php | 2 +- .../Test/Unit/Helper/AbstractHelperTest.php | 2 +- .../Test/Unit/Helper/Mysql/FulltextTest.php | 2 +- .../DB/Test/Unit/Logger/FileTest.php | 2 +- .../DB/Test/Unit/Platform/QuoteTest.php | 2 +- .../Framework/DB/Test/Unit/ProfilerTest.php | 2 +- .../Framework/DB/Test/Unit/QueryTest.php | 2 +- .../Test/Unit/Select/ColumnsRendererTest.php | 2 +- .../Test/Unit/Select/DistinctRendererTest.php | 2 +- .../Unit/Select/ForUpdateRendererTest.php | 2 +- .../DB/Test/Unit/Select/FromRendererTest.php | 2 +- .../DB/Test/Unit/Select/GroupRendererTest.php | 2 +- .../Test/Unit/Select/HavingRendererTest.php | 2 +- .../DB/Test/Unit/Select/LimitRendererTest.php | 2 +- .../DB/Test/Unit/Select/OrderRendererTest.php | 2 +- .../Unit/Select/QueryModifierFactoryTest.php | 106 + .../DB/Test/Unit/Select/RendererProxyTest.php | 2 +- .../Test/Unit/Select/SelectRendererTest.php | 2 +- .../DB/Test/Unit/Select/UnionRendererTest.php | 2 +- .../DB/Test/Unit/Select/WhereRendererTest.php | 2 +- .../DB/Test/Unit/SelectFactoryTest.php | 2 +- .../Framework/DB/Test/Unit/SelectTest.php | 2 +- .../DB/Test/Unit/Sql/LimitExpressionTest.php | 2 +- .../DB/Test/Unit/Sql/UnionExpressionTest.php | 2 +- .../Test/Unit/TemporaryTableServiceTest.php | 209 + .../Framework/DB/Test/Unit/Tree/NodeTest.php | 2 +- .../Magento/Framework/DB/Transaction.php | 2 +- lib/internal/Magento/Framework/DB/Tree.php | 2 +- .../Magento/Framework/DB/Tree/Node.php | 2 +- .../Magento/Framework/DB/Tree/NodeSet.php | 2 +- .../Framework/Data/AbstractCriteria.php | 2 +- .../Framework/Data/AbstractDataObject.php | 2 +- .../Data/AbstractSearchCriteriaBuilder.php | 2 +- .../Framework/Data/AbstractSearchResult.php | 2 +- .../Data/Argument/Interpreter/ArrayType.php | 78 +- .../Data/Argument/Interpreter/Boolean.php | 2 +- .../Data/Argument/Interpreter/Composite.php | 2 +- .../Data/Argument/Interpreter/Constant.php | 2 +- .../Data/Argument/Interpreter/DataObject.php | 2 +- .../Data/Argument/Interpreter/NullType.php | 2 +- .../Data/Argument/Interpreter/Number.php | 2 +- .../Data/Argument/Interpreter/StringUtils.php | 2 +- .../Data/Argument/InterpreterInterface.php | 2 +- .../Magento/Framework/Data/Collection.php | 2 +- .../Framework/Data/Collection/AbstractDb.php | 2 +- .../Collection/Db/FetchStrategy/Cache.php | 2 +- .../Collection/Db/FetchStrategy/Query.php | 2 +- .../Collection/Db/FetchStrategyInterface.php | 2 +- .../Data/Collection/EntityFactory.php | 2 +- .../Collection/EntityFactoryInterface.php | 2 +- .../Framework/Data/Collection/Filesystem.php | 2 +- .../Data/Collection/ModelFactory.php | 2 +- .../Data/CollectionDataSourceInterface.php | 6 +- .../Magento/Framework/Data/DataArray.php | 2 +- lib/internal/Magento/Framework/Data/Form.php | 2 +- .../Framework/Data/Form/AbstractForm.php | 2 +- .../Data/Form/Element/AbstractElement.php | 2 +- .../Framework/Data/Form/Element/Button.php | 2 +- .../Framework/Data/Form/Element/Checkbox.php | 2 +- .../Data/Form/Element/Checkboxes.php | 2 +- .../Data/Form/Element/Collection.php | 2 +- .../Data/Form/Element/CollectionFactory.php | 2 +- .../Framework/Data/Form/Element/Column.php | 2 +- .../Framework/Data/Form/Element/Date.php | 2 +- .../Data/Form/Element/Editablemultiselect.php | 2 +- .../Framework/Data/Form/Element/Editor.php | 2 +- .../Framework/Data/Form/Element/Factory.php | 2 +- .../Framework/Data/Form/Element/Fieldset.php | 2 +- .../Framework/Data/Form/Element/File.php | 2 +- .../Framework/Data/Form/Element/Gallery.php | 2 +- .../Framework/Data/Form/Element/Hidden.php | 2 +- .../Framework/Data/Form/Element/Image.php | 2 +- .../Framework/Data/Form/Element/Imagefile.php | 2 +- .../Framework/Data/Form/Element/Label.php | 2 +- .../Framework/Data/Form/Element/Link.php | 2 +- .../Framework/Data/Form/Element/Multiline.php | 2 +- .../Data/Form/Element/Multiselect.php | 2 +- .../Framework/Data/Form/Element/Note.php | 2 +- .../Framework/Data/Form/Element/Obscure.php | 2 +- .../Framework/Data/Form/Element/Password.php | 2 +- .../Framework/Data/Form/Element/Radio.php | 2 +- .../Framework/Data/Form/Element/Radios.php | 2 +- .../Element/Renderer/RendererInterface.php | 2 +- .../Framework/Data/Form/Element/Reset.php | 2 +- .../Framework/Data/Form/Element/Select.php | 2 +- .../Framework/Data/Form/Element/Submit.php | 2 +- .../Framework/Data/Form/Element/Text.php | 2 +- .../Framework/Data/Form/Element/Textarea.php | 2 +- .../Framework/Data/Form/Element/Time.php | 2 +- .../Framework/Data/Form/ElementFactory.php | 2 +- .../Framework/Data/Form/Filter/Date.php | 2 +- .../Framework/Data/Form/Filter/Escapehtml.php | 2 +- .../Data/Form/Filter/FilterInterface.php | 2 +- .../Framework/Data/Form/Filter/Striptags.php | 2 +- .../Framework/Data/Form/FilterFactory.php | 2 +- .../Magento/Framework/Data/Form/FormKey.php | 2 +- .../Framework/Data/Form/FormKey/Validator.php | 2 +- .../Magento/Framework/Data/FormFactory.php | 2 +- lib/internal/Magento/Framework/Data/Graph.php | 2 +- .../Framework/Data/Helper/PostHelper.php | 2 +- .../Magento/Framework/Data/ObjectFactory.php | 2 +- .../Framework/Data/OptionSourceInterface.php | 2 +- .../Magento/Framework/Data/Schema.php | 2 +- .../Framework/Data/SearchResultInterface.php | 2 +- .../Framework/Data/SearchResultIterator.php | 2 +- .../Data/SearchResultIteratorFactory.php | 2 +- .../Framework/Data/SearchResultProcessor.php | 2 +- .../Data/SearchResultProcessorFactory.php | 2 +- .../Data/SearchResultProcessorInterface.php | 2 +- .../Magento/Framework/Data/Structure.php | 2 +- .../Data/Test/Unit/AbstractCriteriaTest.php | 2 +- .../Data/Test/Unit/AbstractDataObjectTest.php | 2 +- .../Test/Unit/AbstractSearchResultTest.php | 2 +- .../Argument/Interpreter/ArrayTypeTest.php | 52 +- .../Unit/Argument/Interpreter/BooleanTest.php | 2 +- .../Argument/Interpreter/CompositeTest.php | 2 +- .../Argument/Interpreter/ConstantTest.php | 2 +- .../Argument/Interpreter/NullTypeTest.php | 2 +- .../Unit/Argument/Interpreter/NumberTest.php | 2 +- .../Data/Test/Unit/Argument/XsdTest.php | 2 +- .../Argument/_files/typesInvalidArray.php | 2 +- .../Unit/Argument/_files/types_schema.xsd | 2 +- .../Test/Unit/Argument/_files/types_valid.xml | 2 +- .../Collection/Db/FetchStrategy/CacheTest.php | 2 +- .../Collection/Db/FetchStrategy/QueryTest.php | 2 +- .../Test/Unit/Collection/DbCollection.php | 2 +- .../Data/Test/Unit/Collection/DbTest.php | 2 +- .../Test/Unit/Collection/FilesystemTest.php | 2 +- .../Data/Test/Unit/CollectionTest.php | 2 +- .../Data/Test/Unit/Criteria/Sample.php | 2 +- .../Data/Test/Unit/Form/AbstractFormTest.php | 2 +- .../Unit/Form/Element/AbstractElementTest.php | 2 +- .../Test/Unit/Form/Element/ButtonTest.php | 2 +- .../Test/Unit/Form/Element/CheckboxTest.php | 2 +- .../Form/Element/CollectionFactoryTest.php | 2 +- .../Test/Unit/Form/Element/ColumnTest.php | 2 +- .../Data/Test/Unit/Form/Element/DateTest.php | 2 +- .../Form/Element/EditablemultiselectTest.php | 2 +- .../Test/Unit/Form/Element/EditorTest.php | 2 +- .../Test/Unit/Form/Element/FactoryTest.php | 2 +- .../Data/Test/Unit/Form/Element/FileTest.php | 2 +- .../Test/Unit/Form/Element/HiddenTest.php | 2 +- .../Data/Test/Unit/Form/Element/ImageTest.php | 2 +- .../Test/Unit/Form/Element/ImagefileTest.php | 2 +- .../Data/Test/Unit/Form/Element/LabelTest.php | 2 +- .../Data/Test/Unit/Form/Element/LinkTest.php | 2 +- .../Test/Unit/Form/Element/MultilineTest.php | 2 +- .../Unit/Form/Element/MultiselectTest.php | 2 +- .../Data/Test/Unit/Form/Element/NoteTest.php | 2 +- .../Test/Unit/Form/Element/ObscureTest.php | 2 +- .../Test/Unit/Form/Element/PasswordTest.php | 2 +- .../Data/Test/Unit/Form/Element/RadioTest.php | 2 +- .../Data/Test/Unit/Form/Element/ResetTest.php | 2 +- .../Test/Unit/Form/Element/SubmitTest.php | 2 +- .../Data/Test/Unit/Form/Element/TextTest.php | 2 +- .../Test/Unit/Form/Element/TextareaTest.php | 2 +- .../Data/Test/Unit/Form/FilterFactoryTest.php | 2 +- .../Test/Unit/Form/FormKey/ValidatorTest.php | 2 +- .../Data/Test/Unit/Form/FormKeyTest.php | 2 +- .../Data/Test/Unit/FormFactoryTest.php | 2 +- .../Framework/Data/Test/Unit/FormTest.php | 2 +- .../Framework/Data/Test/Unit/GraphTest.php | 2 +- .../Data/Test/Unit/Helper/PostHelperTest.php | 2 +- .../Test/Unit/SearchCriteriaBuilderTest.php | 2 +- .../Test/Unit/SearchResultProcessorTest.php | 2 +- .../Data/Test/Unit/StructureTest.php | 2 +- .../Data/Test/Unit/Stub/DataObject.php | 2 +- .../Test/Unit/Stub/SearchCriteriaBuilder.php | 2 +- .../Data/Test/Unit/Stub/SearchResult.php | 2 +- .../Test/Unit/Tree/Node/CollectionTest.php | 2 +- .../Framework/Data/Test/Unit/TreeTest.php | 2 +- lib/internal/Magento/Framework/Data/Tree.php | 2 +- .../Magento/Framework/Data/Tree/Db.php | 2 +- .../Magento/Framework/Data/Tree/Dbp.php | 2 +- .../Magento/Framework/Data/Tree/Node.php | 2 +- .../Framework/Data/Tree/Node/Collection.php | 2 +- .../Framework/Data/Tree/NodeFactory.php | 2 +- .../Magento/Framework/Data/TreeFactory.php | 2 +- .../Framework/Data/ValueSourceInterface.php | 2 +- .../Framework/Data/etc/argument/types.xsd | 2 +- lib/internal/Magento/Framework/DataObject.php | 2 +- .../Magento/Framework/DataObject/Cache.php | 2 +- .../Magento/Framework/DataObject/Copy.php | 2 +- .../Framework/DataObject/Copy/Config.php | 2 +- .../DataObject/Copy/Config/Converter.php | 2 +- .../Framework/DataObject/Copy/Config/Data.php | 7 +- .../DataObject/Copy/Config/Data/Proxy.php | 2 +- .../DataObject/Copy/Config/Reader.php | 2 +- .../DataObject/Copy/Config/SchemaLocator.php | 2 +- .../Magento/Framework/DataObject/Factory.php | 2 +- .../DataObject/IdentityGeneratorInterface.php | 2 +- .../DataObject/IdentityInterface.php | 2 +- .../Framework/DataObject/IdentityService.php | 2 +- .../DataObject/KeyValueObjectInterface.php | 2 +- .../Magento/Framework/DataObject/Mapper.php | 2 +- .../DataObject/Test/Unit/CacheTest.php | 2 +- .../Test/Unit/Copy/Config/ConverterTest.php | 2 +- .../Unit/Copy/Config/SchemaLocatorTest.php | 2 +- .../Test/Unit/Copy/Config/_files/fieldset.xml | 2 +- .../Copy/Config/_files/fieldset_config.php | 2 +- .../DataObject/Test/Unit/Copy/ConfigTest.php | 2 +- .../DataObject/Test/Unit/CopyTest.php | 2 +- .../DataObject/Test/Unit/MapperTest.php | 2 +- .../Framework/DataObject/etc/fieldset.xsd | 4 +- .../DataObject/etc/fieldset_file.xsd | 2 +- lib/internal/Magento/Framework/Debug.php | 2 +- .../DomDocument/DomDocumentFactory.php | 2 +- .../Magento/Framework/Encryption/Crypt.php | 2 +- .../Framework/Encryption/Encryptor.php | 2 +- .../Encryption/EncryptorInterface.php | 2 +- .../Framework/Encryption/Helper/Security.php | 2 +- .../Test/Unit/Crypt/_files/_cipher_info.php | 2 +- .../Unit/Crypt/_files/_crypt_fixtures.php | 2 +- .../Encryption/Test/Unit/CryptTest.php | 2 +- .../Encryption/Test/Unit/EncryptorTest.php | 2 +- .../Test/Unit/Helper/SecurityTest.php | 2 +- .../Encryption/Test/Unit/UrlCoderTest.php | 2 +- .../Magento/Framework/Encryption/UrlCoder.php | 2 +- .../EntityManager/AbstractModelHydrator.php | 2 +- .../EntityManager/CallbackHandler.php | 2 +- .../EntityManager/CompositeMapper.php | 2 +- .../Framework/EntityManager/Db/CreateRow.php | 21 +- .../Framework/EntityManager/Db/DeleteRow.php | 2 +- .../Framework/EntityManager/Db/ReadRow.php | 2 +- .../Framework/EntityManager/Db/UpdateRow.php | 19 +- .../Framework/EntityManager/EntityManager.php | 2 +- .../EntityManager/EntityMetadata.php | 2 +- .../EntityManager/EntityMetadataInterface.php | 2 +- .../Framework/EntityManager/EventManager.php | 2 +- .../Framework/EntityManager/Hydrator.php | 2 +- .../EntityManager/HydratorInterface.php | 2 +- .../Framework/EntityManager/HydratorPool.php | 2 +- .../Framework/EntityManager/Mapper.php | 2 +- .../EntityManager/MapperInterface.php | 2 +- .../Framework/EntityManager/MapperPool.php | 2 +- .../Framework/EntityManager/MetadataPool.php | 2 +- .../Observer/AfterEntityDelete.php | 2 +- .../Observer/AfterEntityLoad.php | 3 +- .../Observer/AfterEntitySave.php | 2 +- .../Observer/BeforeEntityDelete.php | 2 +- .../Observer/BeforeEntityLoad.php | 32 + .../Observer/BeforeEntitySave.php | 2 +- .../Operation/AttributeInterface.php | 2 +- .../EntityManager/Operation/AttributePool.php | 2 +- .../EntityManager/Operation/CheckIfExists.php | 2 +- .../Operation/CheckIfExistsInterface.php | 2 +- .../EntityManager/Operation/Create.php | 10 +- .../Operation/Create/CreateAttributes.php | 2 +- .../Operation/Create/CreateExtensions.php | 2 +- .../Operation/Create/CreateMain.php | 2 +- .../Operation/CreateInterface.php | 2 +- .../EntityManager/Operation/Delete.php | 2 +- .../Operation/Delete/DeleteAttributes.php | 2 +- .../Operation/Delete/DeleteExtensions.php | 2 +- .../Operation/Delete/DeleteMain.php | 2 +- .../Operation/DeleteInterface.php | 2 +- .../Operation/ExtensionInterface.php | 2 +- .../EntityManager/Operation/ExtensionPool.php | 2 +- .../EntityManager/Operation/Read.php | 11 +- .../Operation/Read/ReadAttributes.php | 2 +- .../Operation/Read/ReadExtensions.php | 2 +- .../EntityManager/Operation/Read/ReadMain.php | 2 +- .../EntityManager/Operation/ReadInterface.php | 2 +- .../EntityManager/Operation/Update.php | 9 +- .../Operation/Update/UpdateAttributes.php | 2 +- .../Operation/Update/UpdateExtensions.php | 2 +- .../Operation/Update/UpdateMain.php | 2 +- .../Operation/UpdateInterface.php | 2 +- .../EntityManager/Operation/ValidatorPool.php | 2 +- .../EntityManager/OperationInterface.php | 2 +- .../Framework/EntityManager/OperationPool.php | 2 +- .../EntityManager/Sequence/Sequence.php | 2 +- .../Sequence/SequenceFactory.php | 2 +- .../Sequence/SequenceManager.php | 2 +- .../Sequence/SequenceRegistry.php | 2 +- .../Test/Unit/Db/UpdateRowTest.php | 108 +- .../EntityManager/Test/Unit/MapperTest.php | 2 +- .../Test/Unit/Operation/CreateTest.php | 78 + .../Test/Unit/Operation/UpdateTest.php | 78 + .../Test/Unit/TypeResolverTest.php | 2 +- .../Framework/EntityManager/TypeResolver.php | 2 +- lib/internal/Magento/Framework/Escaper.php | 2 +- lib/internal/Magento/Framework/Event.php | 2 +- .../Magento/Framework/Event/Collection.php | 2 +- .../Magento/Framework/Event/Config.php | 2 +- .../Framework/Event/Config/Converter.php | 2 +- .../Magento/Framework/Event/Config/Data.php | 19 +- .../Magento/Framework/Event/Config/Reader.php | 2 +- .../Framework/Event/Config/SchemaLocator.php | 2 +- .../Framework/Event/ConfigInterface.php | 2 +- .../Event/Invoker/InvokerDefault.php | 2 +- .../Framework/Event/InvokerInterface.php | 2 +- .../Magento/Framework/Event/Manager.php | 2 +- .../Framework/Event/ManagerInterface.php | 2 +- .../Magento/Framework/Event/Observer.php | 2 +- .../Framework/Event/Observer/Collection.php | 2 +- .../Magento/Framework/Event/Observer/Cron.php | 2 +- .../Framework/Event/Observer/Regex.php | 2 +- .../Framework/Event/ObserverFactory.php | 2 +- .../Framework/Event/ObserverInterface.php | 2 +- .../Event/Test/Unit/CollectionTest.php | 2 +- .../Event/Test/Unit/Config/ConverterTest.php | 2 +- .../Test/Unit/Config/SchemaLocatorTest.php | 2 +- .../Event/Test/Unit/Config/XsdTest.php | 2 +- .../Test/Unit/Config/_files/event_config.php | 2 +- .../Test/Unit/Config/_files/event_config.xml | 4 +- .../Config/_files/event_invalid_config.xml | 4 +- .../Config/_files/invalidEventsXmlArray.php | 2 +- .../Test/Unit/Config/_files/valid_events.xml | 2 +- .../Framework/Event/Test/Unit/ConfigTest.php | 2 +- .../Test/Unit/Invoker/InvokerDefaultTest.php | 2 +- .../Test/Unit/Invoker/ObserverExample.php | 2 +- .../Framework/Event/Test/Unit/ManagerStub.php | 2 +- .../Framework/Event/Test/Unit/ManagerTest.php | 2 +- .../Test/Unit/Observer/CollectionTest.php | 2 +- .../Event/Test/Unit/Observer/CronTest.php | 2 +- .../Event/Test/Unit/Observer/RegexTest.php | 2 +- .../Event/Test/Unit/ObserverFactoryTest.php | 2 +- .../Event/Test/Unit/ObserverTest.php | 2 +- .../Event/Test/Unit/WrapperFactoryTest.php | 2 +- .../Framework/Event/WrapperFactory.php | 2 +- .../Magento/Framework/Event/etc/events.xsd | 2 +- .../Magento/Framework/EventFactory.php | 2 +- .../Exception/AbstractAggregateException.php | 2 +- .../Exception/AlreadyExistsException.php | 19 +- .../Exception/AuthenticationException.php | 2 +- .../Exception/AuthorizationException.php | 2 +- .../ConfigurationMismatchException.php | 2 +- .../Exception/CouldNotDeleteException.php | 2 +- .../Exception/CouldNotSaveException.php | 2 +- .../Framework/Exception/CronException.php | 2 +- .../Exception/EmailNotConfirmedException.php | 2 +- .../Exception/FileSystemException.php | 2 +- .../Framework/Exception/InputException.php | 2 +- .../Exception/IntegrationException.php | 2 +- .../InvalidEmailOrPasswordException.php | 2 +- .../Exception/LocalizedException.php | 2 +- .../Framework/Exception/MailException.php | 2 +- .../Exception/NoSuchEntityException.php | 2 +- .../Framework/Exception/NotFoundException.php | 2 +- .../Framework/Exception/PaymentException.php | 2 +- .../Plugin/AuthenticationException.php | 2 +- .../RemoteServiceUnavailableException.php | 2 +- .../Exception/SecurityViolationException.php | 2 +- .../Exception/SerializationException.php | 2 +- .../Framework/Exception/SessionException.php | 2 +- .../Exception/State/ExpiredException.php | 2 +- .../Exception/State/InitException.php | 2 +- .../State/InputMismatchException.php | 2 +- .../State/InvalidTransitionException.php | 2 +- .../Exception/State/UserLockedException.php | 2 +- .../Framework/Exception/StateException.php | 2 +- .../TemporaryState/CouldNotSaveException.php | 2 +- .../TemporaryStateExceptionInterface.php | 2 +- .../Test/Unit/AuthenticationExceptionTest.php | 2 +- .../Test/Unit/AuthorizationExceptionTest.php | 2 +- .../Unit/EmailNotConfirmedExceptionTest.php | 2 +- .../Test/Unit/InputExceptionTest.php | 2 +- .../Test/Unit/LocalizedExceptionTest.php | 2 +- .../Test/Unit/NoSuchEntityExceptionTest.php | 2 +- .../Test/Unit/State/ExpiredExceptionTest.php | 2 +- .../Unit/State/InputMismatchExceptionTest.php | 2 +- .../State/InvalidTransitionExceptionTest.php | 2 +- .../Test/Unit/StateExceptionTest.php | 2 +- .../Exception/ValidatorException.php | 2 +- lib/internal/Magento/Framework/File/Csv.php | 2 +- .../Magento/Framework/File/CsvMulty.php | 2 +- lib/internal/Magento/Framework/File/Mime.php | 4 +- lib/internal/Magento/Framework/File/Size.php | 2 +- .../Framework/File/Test/Unit/CsvTest.php | 2 +- .../Framework/File/Test/Unit/MimeTest.php | 3 +- .../Test/Unit/Transfer/Adapter/HttpTest.php | 2 +- .../File/Test/Unit/_files/UPPERCASE.WEIRD | 0 .../File/Test/Unit/_files/javascript.js | 2 +- .../Framework/File/Transfer/Adapter/Http.php | 2 +- .../Magento/Framework/File/Uploader.php | 2 +- .../Framework/File/UploaderFactory.php | 2 +- lib/internal/Magento/Framework/Filesystem.php | 2 +- .../Framework/Filesystem/Directory/Read.php | 2 +- .../Filesystem/Directory/ReadFactory.php | 2 +- .../Filesystem/Directory/ReadInterface.php | 2 +- .../Framework/Filesystem/Directory/Write.php | 4 +- .../Filesystem/Directory/WriteFactory.php | 2 +- .../Filesystem/Directory/WriteInterface.php | 2 +- .../Framework/Filesystem/DirectoryList.php | 2 +- .../Framework/Filesystem/Driver/File.php | 2 +- .../Framework/Filesystem/Driver/Http.php | 2 +- .../Framework/Filesystem/Driver/Https.php | 2 +- .../Framework/Filesystem/Driver/Zlib.php | 2 +- .../Framework/Filesystem/DriverInterface.php | 2 +- .../Framework/Filesystem/DriverPool.php | 2 +- .../Framework/Filesystem/File/Read.php | 2 +- .../Framework/Filesystem/File/ReadFactory.php | 2 +- .../Filesystem/File/ReadInterface.php | 2 +- .../Framework/Filesystem/File/Write.php | 2 +- .../Filesystem/File/WriteFactory.php | 2 +- .../Filesystem/File/WriteInterface.php | 2 +- .../Framework/Filesystem/FileResolver.php | 2 +- .../Filesystem/Filter/ExcludeFilter.php | 2 +- .../Magento/Framework/Filesystem/Glob.php | 2 +- .../Framework/Filesystem/Io/AbstractIo.php | 2 +- .../Magento/Framework/Filesystem/Io/File.php | 2 +- .../Magento/Framework/Filesystem/Io/Ftp.php | 2 +- .../Framework/Filesystem/Io/IoInterface.php | 2 +- .../Magento/Framework/Filesystem/Io/Sftp.php | 2 +- .../Test/Unit/Directory/ReadTest.php | 2 +- .../Test/Unit/Directory/WriteTest.php | 72 +- .../Test/Unit/DirectoryListTest.php | 2 +- .../Filesystem/Test/Unit/Driver/FileTest.php | 2 +- .../Filesystem/Test/Unit/Driver/HttpTest.php | 2 +- .../Filesystem/Test/Unit/Driver/HttpsTest.php | 2 +- .../Filesystem/Test/Unit/DriverPoolTest.php | 2 +- .../Test/Unit/File/ExcludeFilterTest.php | 2 +- .../Test/Unit/File/ReadFactoryTest.php | 2 +- .../Filesystem/Test/Unit/File/ReadTest.php | 2 +- .../Test/Unit/File/WriteFactoryTest.php | 2 +- .../Filesystem/Test/Unit/File/WriteTest.php | 2 +- .../Filesystem/Test/Unit/FileResolverTest.php | 2 +- .../Filesystem/Test/Unit/_files/http_mock.php | 2 +- .../Framework/Filter/AbstractFactory.php | 2 +- .../Magento/Framework/Filter/ArrayFilter.php | 2 +- .../Magento/Framework/Filter/DataObject.php | 2 +- .../Framework/Filter/DataObject/Grid.php | 2 +- .../Magento/Framework/Filter/Decrypt.php | 2 +- .../Magento/Framework/Filter/Email.php | 2 +- .../Magento/Framework/Filter/Encrypt.php | 2 +- .../Filter/Encrypt/AdapterInterface.php | 2 +- .../Framework/Filter/Encrypt/Basic.php | 2 +- .../Magento/Framework/Filter/Factory.php | 2 +- .../Framework/Filter/FactoryInterface.php | 2 +- .../Framework/Filter/FilterManager.php | 2 +- .../Framework/Filter/FilterManager/Config.php | 2 +- .../Filter/FilterManager/ConfigInterface.php | 2 +- .../Magento/Framework/Filter/Input.php | 2 +- .../Framework/Filter/Input/MaliciousCode.php | 4 +- .../Filter/LocalizedToNormalized.php | 2 +- .../Magento/Framework/Filter/Money.php | 2 +- .../Framework/Filter/RemoveAccents.php | 2 +- .../Magento/Framework/Filter/RemoveTags.php | 2 +- .../Magento/Framework/Filter/SplitWords.php | 2 +- .../Magento/Framework/Filter/Sprintf.php | 2 +- .../Magento/Framework/Filter/StripTags.php | 2 +- .../Magento/Framework/Filter/Template.php | 2 +- .../Framework/Filter/Template/Simple.php | 2 +- .../Template/Tokenizer/AbstractTokenizer.php | 2 +- .../Filter/Template/Tokenizer/Parameter.php | 2 +- .../Filter/Template/Tokenizer/Variable.php | 2 +- .../Filter/Test/Unit/AbstractFactoryTest.php | 2 +- .../Filter/Test/Unit/ArrayFilterTest.php | 2 +- .../Filter/Test/Unit/DataObject/GridTest.php | 2 +- .../Test/Unit/FilterManager/ConfigTest.php | 2 +- .../Filter/Test/Unit/FilterManagerTest.php | 2 +- .../Test/Unit/Input/MaliciousCodeTest.php | 4 +- .../Framework/Filter/Test/Unit/InputTest.php | 2 +- .../Filter/Test/Unit/RemoveAccentsTest.php | 2 +- .../Filter/Test/Unit/RemoveTagsTest.php | 2 +- .../Filter/Test/Unit/SplitWordsTest.php | 2 +- .../Filter/Test/Unit/SprintfTest.php | 2 +- .../Filter/Test/Unit/StripTagsTest.php | 2 +- .../Filter/Test/Unit/Template/SimpleTest.php | 2 +- .../Unit/Template/Tokenizer/ParameterTest.php | 2 +- .../Unit/Template/Tokenizer/VariableTest.php | 2 +- .../Filter/Test/Unit/TemplateTest.php | 4 +- .../Filter/Test/Unit/TranslitTest.php | 2 +- .../Filter/Test/Unit/TranslitUrlTest.php | 2 +- .../Filter/Test/Unit/TruncateTest.php | 2 +- .../Magento/Framework/Filter/Translit.php | 2 +- .../Magento/Framework/Filter/TranslitUrl.php | 2 +- .../Magento/Framework/Filter/Truncate.php | 2 +- .../Magento/Framework/Filter/ZendFactory.php | 2 +- lib/internal/Magento/Framework/Flag.php | 59 +- .../Magento/Framework/Flag/FlagResource.php | 2 +- .../Magento/Framework/FlagFactory.php | 2 +- .../Magento/Framework/HTTP/Adapter/Curl.php | 60 +- .../HTTP/Adapter/FileTransferFactory.php | 2 +- .../Magento/Framework/HTTP/Authentication.php | 2 +- .../Magento/Framework/HTTP/Client/Curl.php | 34 +- .../Magento/Framework/HTTP/Client/Socket.php | 2 +- .../Magento/Framework/HTTP/ClientFactory.php | 2 +- .../Framework/HTTP/ClientInterface.php | 2 +- .../Magento/Framework/HTTP/Header.php | 2 +- .../HTTP/PhpEnvironment/RemoteAddress.php | 2 +- .../Framework/HTTP/PhpEnvironment/Request.php | 2 +- .../HTTP/PhpEnvironment/Response.php | 2 +- .../HTTP/PhpEnvironment/ServerAddress.php | 2 +- .../HTTP/Test/Unit/Adapter/CurlTest.php | 11 +- .../Unit/Adapter/_files/curl_exec_mock.php | 2 +- .../HTTP/Test/Unit/AuthenticationTest.php | 2 +- .../Framework/HTTP/Test/Unit/HeaderTest.php | 2 +- .../Unit/PhpEnvironment/RemoteAddressTest.php | 2 +- .../Test/Unit/PhpEnvironment/RequestTest.php | 2 +- .../Test/Unit/PhpEnvironment/ResponseTest.php | 2 +- .../Unit/PhpEnvironment/ServerAddressTest.php | 2 +- .../Magento/Framework/HTTP/ZendClient.php | 2 +- lib/internal/Magento/Framework/Image.php | 2 +- .../Image/Adapter/AbstractAdapter.php | 2 +- .../Image/Adapter/AdapterInterface.php | 2 +- .../Framework/Image/Adapter/Config.php | 2 +- .../Image/Adapter/ConfigInterface.php | 2 +- .../Magento/Framework/Image/Adapter/Gd2.php | 6 +- .../Framework/Image/Adapter/ImageMagick.php | 9 +- .../Framework/Image/AdapterFactory.php | 2 +- .../Magento/Framework/Image/Factory.php | 2 +- .../Image/Test/Unit/Adapter/AbstractTest.php | 2 +- .../Image/Test/Unit/Adapter/Gd2Test.php | 2 +- .../Test/Unit/Adapter/ImageMagickTest.php | 2 +- .../Unit/Adapter/_files/global_php_mock.php | 2 +- .../Image/Test/Unit/AdapterFactoryTest.php | 2 +- .../Framework/Indexer/AbstractProcessor.php | 2 +- .../Magento/Framework/Indexer/Action/Base.php | 14 +- .../Framework/Indexer/Action/Dummy.php | 2 +- .../Framework/Indexer/Action/Entity.php | 4 +- .../Framework/Indexer/ActionFactory.php | 2 +- .../Framework/Indexer/ActionInterface.php | 2 +- .../Framework/Indexer/CacheContext.php | 2 +- .../Framework/Indexer/Config/Converter.php | 7 +- .../Framework/Indexer/Config/Reader.php | 2 +- .../Indexer/Config/SchemaLocator.php | 2 +- .../Framework/Indexer/ConfigInterface.php | 2 +- .../Framework/Indexer/FieldsetInterface.php | 2 +- .../Framework/Indexer/FieldsetPool.php | 2 +- .../Framework/Indexer/FilterInterface.php | 2 +- .../Framework/Indexer/GridStructure.php | 2 +- .../Indexer/Handler/AttributeHandler.php | 2 +- .../Indexer/Handler/ConcatHandler.php | 2 +- .../Indexer/Handler/DefaultHandler.php | 2 +- .../Framework/Indexer/HandlerInterface.php | 2 +- .../Magento/Framework/Indexer/HandlerPool.php | 2 +- .../Framework/Indexer/IndexStructure.php | 2 +- .../Indexer/IndexStructureInterface.php | 2 +- .../Framework/Indexer/IndexerInterface.php | 2 +- .../Framework/Indexer/IndexerRegistry.php | 2 +- .../Framework/Indexer/SaveHandler/Batch.php | 18 +- .../Framework/Indexer/SaveHandler/Grid.php | 2 +- .../Indexer/SaveHandler/IndexerHandler.php | 2 +- .../Indexer/SaveHandler/IndexerInterface.php | 2 +- .../Framework/Indexer/SaveHandlerFactory.php | 2 +- .../ScopeResolver/FlatScopeResolver.php | 2 +- .../ScopeResolver/IndexScopeResolver.php | 13 +- .../Framework/Indexer/StateInterface.php | 2 +- .../Framework/Indexer/StructureFactory.php | 2 +- .../Framework/Indexer/Table/Strategy.php | 2 +- .../Indexer/Table/StrategyInterface.php | 2 +- .../Indexer/Test/Unit/ActionFactoryTest.php | 2 +- .../Framework/Indexer/Test/Unit/BatchTest.php | 5 +- .../Test/Unit/Config/ConverterTest.php | 2 +- .../Indexer/Test/Unit/Config/ReaderTest.php | 2 +- .../Test/Unit/Config/SchemaLocatorTest.php | 2 +- .../Indexer/Test/Unit/GridStructureTest.php | 2 +- .../Indexer/Test/Unit/IndexStructureTest.php | 2 +- .../Indexer/Test/Unit/IndexerRegistryTest.php | 2 +- .../ScopeResolver/IndexScopeResolverTest.php | 2 +- .../Indexer/Test/Unit/StrategyTest.php | 2 +- .../Framework/Indexer/Test/Unit/XsdTest.php | 2 +- .../Test/Unit/_files/indexer_config.php | 2 +- .../Test/Unit/_files/indexer_merged_one.xml | 4 +- .../Test/Unit/_files/indexer_merged_two.xml | 4 +- .../Indexer/Test/Unit/_files/indexer_one.xml | 4 +- .../Test/Unit/_files/indexer_three.xml | 4 +- .../Indexer/Test/Unit/_files/indexer_two.xml | 4 +- .../Unit/_files/invalidIndexerXmlArray.php | 2 +- .../Test/Unit/_files/valid_indexer.xml | 4 +- .../Magento/Framework/Indexer/etc/indexer.xsd | 2 +- .../Framework/Indexer/etc/indexer_merged.xsd | 2 +- .../Code/Generator/Interceptor.php | 2 +- .../Interception/Code/InterfaceValidator.php | 2 +- .../Framework/Interception/Config/Config.php | 23 +- .../Interception/ConfigInterface.php | 2 +- .../Interception/Definition/Compiled.php | 39 - .../Interception/Definition/Runtime.php | 2 +- .../Interception/DefinitionInterface.php | 2 +- .../Framework/Interception/Interceptor.php | 2 +- .../Interception/InterceptorInterface.php | 2 +- .../ObjectManager/Config/Compiled.php | 2 +- .../ObjectManager/Config/Developer.php | 2 +- .../ObjectManager/ConfigInterface.php | 2 +- .../Interception/PluginList/PluginList.php | 90 +- .../Interception/PluginListInterface.php | 2 +- .../Unit/Code/Generator/InterceptorTest.php | 2 +- .../Test/Unit/Code/InterfaceValidatorTest.php | 2 +- .../Test/Unit/Config/ConfigTest.php | 80 +- .../Module/Model/InterfaceValidator/Item.php | 2 +- .../ItemPlugin/ExtraParameters.php | 2 +- .../ItemPlugin/IncompatibleArgumentsCount.php | 2 +- .../ItemPlugin/IncompatibleArgumentsType.php | 2 +- .../ItemPlugin/IncompatibleInterface.php | 2 +- .../ItemPlugin/IncorrectSubject.php | 2 +- .../ItemPlugin/InvalidProceed.php | 2 +- .../ItemPlugin/ValidPlugin.php | 2 +- .../InterfaceValidator/ItemWithArguments.php | 2 +- .../Test/Unit/Custom/Module/Model/Item.php | 2 +- .../Custom/Module/Model/Item/Enhanced.php | 2 +- .../Custom/Module/Model/ItemContainer.php | 2 +- .../Module/Model/ItemContainer/Enhanced.php | 2 +- .../Model/ItemContainerPlugin/Simple.php | 2 +- .../Module/Model/ItemPlugin/Advanced.php | 2 +- .../Custom/Module/Model/ItemPlugin/Simple.php | 2 +- .../Custom/Module/Model/StartingBackslash.php | 2 +- .../Module/Model/StartingBackslash/Plugin.php | 2 +- .../Test/Unit/Definition/CompiledTest.php | 24 - .../ObjectManager/Config/DeveloperTest.php | 2 +- .../Test/Unit/PluginList/PluginListTest.php | 175 +- .../Test/Unit/_files/reader_mock_map.php | 13 +- .../Framework/Intl/DateTimeFactory.php | 2 +- .../Magento/Framework/Json/Decoder.php | 2 +- .../Framework/Json/DecoderInterface.php | 2 +- .../Magento/Framework/Json/Encoder.php | 2 +- .../Framework/Json/EncoderInterface.php | 2 +- .../Magento/Framework/Json/Helper/Data.php | 2 +- .../Json/Test/Unit/Helper/DataTest.php | 2 +- .../Locale/Bundle/CurrencyBundle.php | 2 +- .../Framework/Locale/Bundle/DataBundle.php | 2 +- .../Locale/Bundle/LanguageBundle.php | 2 +- .../Framework/Locale/Bundle/RegionBundle.php | 2 +- .../Locale/Bundle/TimezoneBundle.php | 2 +- .../Magento/Framework/Locale/Config.php | 3 +- .../Framework/Locale/ConfigInterface.php | 2 +- .../Magento/Framework/Locale/Currency.php | 2 +- .../Framework/Locale/CurrencyInterface.php | 2 +- .../Magento/Framework/Locale/Format.php | 2 +- .../Framework/Locale/FormatInterface.php | 2 +- .../Framework/Locale/ListsInterface.php | 2 +- .../Magento/Framework/Locale/Resolver.php | 2 +- .../Framework/Locale/ResolverInterface.php | 2 +- .../Framework/Locale/Test/Unit/ConfigTest.php | 2 +- .../Locale/Test/Unit/CurrencyTest.php | 2 +- .../Locale/Test/Unit/TranslatedListsTest.php | 2 +- .../Framework/Locale/TranslatedLists.php | 2 +- .../Magento/Framework/Logger/Handler/Base.php | 2 +- .../Framework/Logger/Handler/Debug.php | 2 +- .../Framework/Logger/Handler/Exception.php | 2 +- .../Framework/Logger/Handler/System.php | 2 +- .../Magento/Framework/Logger/Monolog.php | 2 +- .../Magento/Framework/Mail/Message.php | 2 +- .../Framework/Mail/MessageInterface.php | 2 +- .../Mail/Template/ConfigInterface.php | 2 +- .../Framework/Mail/Template/Factory.php | 2 +- .../Mail/Template/FactoryInterface.php | 2 +- .../Mail/Template/SenderResolverInterface.php | 2 +- .../Mail/Template/TransportBuilder.php | 2 +- .../Framework/Mail/TemplateInterface.php | 2 +- .../Framework/Mail/Test/Unit/MessageTest.php | 2 +- .../Mail/Test/Unit/Template/FactoryTest.php | 2 +- .../Unit/Template/TransportBuilderTest.php | 2 +- .../Mail/Test/Unit/TransportTest.php | 2 +- .../Magento/Framework/Mail/Transport.php | 2 +- .../Framework/Mail/TransportInterface.php | 2 +- .../Mail/TransportInterfaceFactory.php | 2 +- .../Magento/Framework/Math/Calculator.php | 2 +- .../Magento/Framework/Math/Division.php | 2 +- .../Magento/Framework/Math/Random.php | 2 +- .../Math/Test/Unit/CalculatorTest.php | 2 +- .../Framework/Math/Test/Unit/DivisionTest.php | 2 +- .../Framework/Math/Test/Unit/RandomTest.php | 2 +- .../Framework/Message/AbstractMessage.php | 2 +- .../Magento/Framework/Message/Collection.php | 2 +- .../Framework/Message/CollectionFactory.php | 2 +- .../Magento/Framework/Message/Error.php | 2 +- .../Magento/Framework/Message/Factory.php | 2 +- .../Magento/Framework/Message/Manager.php | 2 +- .../Framework/Message/ManagerInterface.php | 2 +- .../Framework/Message/MessageInterface.php | 2 +- .../Magento/Framework/Message/Notice.php | 2 +- .../Framework/Message/PhraseFactory.php | 2 +- .../Magento/Framework/Message/Session.php | 2 +- .../Magento/Framework/Message/Success.php | 2 +- .../Message/Test/Unit/AbstractMessageTest.php | 2 +- .../Message/Test/Unit/CollectionTest.php | 2 +- .../Framework/Message/Test/Unit/ErrorTest.php | 2 +- .../Message/Test/Unit/FactoryTest.php | 2 +- .../Message/Test/Unit/ManagerTest.php | 2 +- .../Message/Test/Unit/NoticeTest.php | 2 +- .../Message/Test/Unit/SuccessTest.php | 2 +- .../Message/Test/Unit/TestingMessage.php | 2 +- .../Message/Test/Unit/WarningTest.php | 2 +- .../Magento/Framework/Message/Warning.php | 2 +- .../Model/AbstractExtensibleModel.php | 10 +- .../Magento/Framework/Model/AbstractModel.php | 20 +- .../Model/ActionValidator/RemoveAction.php | 2 +- .../ActionValidator/RemoveAction/Allowed.php | 2 +- .../Magento/Framework/Model/CallbackPool.php | 2 +- .../Magento/Framework/Model/Context.php | 2 +- .../Model/Entity/RepositoryFactory.php | 2 +- .../Magento/Framework/Model/Entity/Scope.php | 2 +- .../Framework/Model/Entity/ScopeFactory.php | 2 +- .../Framework/Model/Entity/ScopeInterface.php | 2 +- .../Model/Entity/ScopeProviderInterface.php | 2 +- .../Framework/Model/Entity/ScopeResolver.php | 2 +- .../Framework/Model/EntityRegistry.php | 2 +- .../Framework/Model/EntitySnapshot.php | 2 +- .../EntitySnapshot/AttributeProvider.php | 2 +- .../AttributeProviderInterface.php | 2 +- .../Model/Operation/ReadInterface.php | 2 +- .../Model/Operation/WriteInterface.php | 2 +- .../Model/ResourceModel/AbstractResource.php | 35 +- .../Model/ResourceModel/Db/AbstractDb.php | 27 +- .../Db/Collection/AbstractCollection.php | 2 +- .../Model/ResourceModel/Db/Context.php | 2 +- .../ResourceModel/Db/CreateEntityRow.php | 2 +- .../ResourceModel/Db/DeleteEntityRow.php | 2 +- .../Db/ObjectRelationProcessor.php | 2 +- .../Db/ProcessEntityRelationInterface.php | 2 +- .../Model/ResourceModel/Db/Profiler.php | 2 +- .../Model/ResourceModel/Db/ReadEntityRow.php | 2 +- .../ResourceModel/Db/Relation/ActionPool.php | 2 +- .../ResourceModel/Db/TransactionManager.php | 2 +- .../Db/TransactionManagerInterface.php | 2 +- .../ResourceModel/Db/UpdateEntityRow.php | 2 +- .../Db/ValidateDataIntegrity.php | 2 +- .../Db/VersionControl/AbstractDb.php | 2 +- .../Db/VersionControl/Collection.php | 2 +- .../Db/VersionControl/Metadata.php | 2 +- .../Db/VersionControl/RelationComposite.php | 2 +- .../Db/VersionControl/RelationInterface.php | 2 +- .../Db/VersionControl/Snapshot.php | 2 +- .../ResourceModel/Entity/AbstractEntity.php | 2 +- .../Model/ResourceModel/Entity/Table.php | 2 +- .../Model/ResourceModel/Iterator.php | 2 +- .../Model/ResourceModel/Type/AbstractType.php | 2 +- .../Framework/Model/ResourceModel/Type/Db.php | 2 +- .../Type/Db/ConnectionFactory.php | 2 +- .../Type/Db/ConnectionFactoryInterface.php | 2 +- .../Model/ResourceModel/Type/Db/Pdo/Mysql.php | 2 +- .../Test/Unit/AbstractExtensibleModelTest.php | 2 +- .../Model/Test/Unit/AbstractModelTest.php | 2 +- .../Unit/ActionValidator/RemoveActionTest.php | 2 +- .../EntitySnapshot/AttributeProviderTest.php | 2 +- .../ResourceModel/AbstractResourceStub.php | 2 +- .../ResourceModel/AbstractResourceTest.php | 213 +- .../Unit/ResourceModel/Db/AbstractDbTest.php | 123 +- .../Db/Collection/AbstractCollectionTest.php | 2 +- .../ResourceModel/Db/CreateEntityRowTest.php | 2 +- .../ResourceModel/Db/DeleteEntityRowTest.php | 2 +- .../ResourceModel/Db/ReadEntityRowTest.php | 2 +- .../Db/Relation/ActionPoolTest.php | 2 +- .../ResourceModel/Db/UpdateEntityRowTest.php | 2 +- .../Db/VersionControl/MetadataTest.php | 2 +- .../VersionControl/RelationCompositeTest.php | 2 +- .../Db/VersionControl/SnapshotTest.php | 2 +- .../Type/Db/ConnectionFactoryTest.php | 2 +- .../ResourceModel/Type/Db/Pdo/MysqlTest.php | 2 +- .../Framework/Module/ConflictChecker.php | 2 +- .../Framework/Module/DbVersionInfo.php | 2 +- .../Module/Declaration/Converter/Dom.php | 2 +- .../Framework/Module/DependencyChecker.php | 2 +- lib/internal/Magento/Framework/Module/Dir.php | 2 +- .../Magento/Framework/Module/Dir/Reader.php | 2 +- .../Framework/Module/Dir/ReverseResolver.php | 2 +- .../Framework/Module/FullModuleList.php | 2 +- .../Magento/Framework/Module/Manager.php | 87 +- .../Magento/Framework/Module/ModuleList.php | 2 +- .../Framework/Module/ModuleList/Loader.php | 2 +- .../Framework/Module/ModuleListInterface.php | 2 +- .../Framework/Module/ModuleResource.php | 2 +- .../Framework/Module/Output/Config.php | 26 +- .../Module/Output/ConfigInterface.php | 9 +- .../Magento/Framework/Module/PackageInfo.php | 2 +- .../Framework/Module/PackageInfoFactory.php | 2 +- .../Module/Plugin/DbStatusValidator.php | 2 +- .../Framework/Module/ResourceInterface.php | 2 +- .../Magento/Framework/Module/Setup.php | 2 +- .../Framework/Module/Setup/Context.php | 2 +- .../Framework/Module/Setup/Migration.php | 2 +- .../Framework/Module/Setup/MigrationData.php | 2 +- .../Module/Setup/MigrationFactory.php | 2 +- .../Magento/Framework/Module/Status.php | 2 +- .../Module/Test/Unit/ConflictCheckerTest.php | 2 +- .../Module/Test/Unit/DbVersionInfoTest.php | 2 +- .../Unit/Declaration/Converter/DomTest.php | 2 +- .../_files/converted_valid_module.php | 2 +- .../Converter/_files/valid_module.xml | 2 +- .../Test/Unit/DependencyCheckerTest.php | 2 +- .../Module/Test/Unit/Dir/ReaderTest.php | 2 +- .../Test/Unit/Dir/ReverseResolverTest.php | 2 +- .../Framework/Module/Test/Unit/DirTest.php | 2 +- .../Module/Test/Unit/FullModuleListTest.php | 2 +- .../Module/Test/Unit/ManagerTest.php | 111 +- .../Test/Unit/ModuleList/LoaderTest.php | 2 +- .../Module/Test/Unit/ModuleListTest.php | 2 +- .../Test/Unit/PackageInfoFactoryTest.php | 2 +- .../Module/Test/Unit/PackageInfoTest.php | 2 +- .../Unit/Plugin/DbStatusValidatorTest.php | 2 +- .../Module/Test/Unit/Setup/MigrationTest.php | 2 +- .../Setup/_files/data_content_plain_model.php | 2 +- .../_files/data_content_plain_pk_fields.php | 2 +- .../_files/data_content_plain_resource.php | 2 +- .../Setup/_files/data_content_serialized.php | 2 +- .../Unit/Setup/_files/data_content_wiki.php | 2 +- .../Unit/Setup/_files/data_content_xml.php | 2 +- .../Framework/Module/Test/Unit/SetupTest.php | 2 +- .../Framework/Module/Test/Unit/StatusTest.php | 2 +- .../Magento/Framework/Module/etc/module.xsd | 2 +- .../Magento/Framework/Mview/ActionFactory.php | 2 +- .../Framework/Mview/ActionInterface.php | 2 +- .../Magento/Framework/Mview/Config.php | 2 +- .../Framework/Mview/Config/Converter.php | 2 +- .../Magento/Framework/Mview/Config/Data.php | 19 +- .../Framework/Mview/Config/Data/Proxy.php | 2 +- .../Magento/Framework/Mview/Config/Reader.php | 2 +- .../Framework/Mview/Config/SchemaLocator.php | 2 +- .../Framework/Mview/ConfigInterface.php | 2 +- .../Magento/Framework/Mview/Processor.php | 2 +- .../Framework/Mview/ProcessorInterface.php | 2 +- .../Mview/Test/Unit/ActionFactoryTest.php | 2 +- .../Mview/Test/Unit/Config/ConverterTest.php | 2 +- .../Mview/Test/Unit/Config/Data/ProxyTest.php | 2 +- .../Mview/Test/Unit/Config/DataTest.php | 47 +- .../Mview/Test/Unit/Config/ReaderTest.php | 2 +- .../Framework/Mview/Test/Unit/ConfigTest.php | 2 +- .../Mview/Test/Unit/ProcessorTest.php | 2 +- .../Mview/Test/Unit/View/ChangelogTest.php | 2 +- .../Mview/Test/Unit/View/CollectionTest.php | 2 +- .../Unit/View/SubscriptionFactoryTest.php | 2 +- .../Mview/Test/Unit/View/SubscriptionTest.php | 67 +- .../Framework/Mview/Test/Unit/ViewTest.php | 2 +- .../Framework/Mview/Test/Unit/XsdTest.php | 2 +- .../Test/Unit/_files/invalidMviewXmlArray.php | 2 +- .../Mview/Test/Unit/_files/mview_config.php | 2 +- .../Test/Unit/_files/mview_merged_one.xml | 2 +- .../Test/Unit/_files/mview_merged_two.xml | 2 +- .../Mview/Test/Unit/_files/mview_one.xml | 2 +- .../Mview/Test/Unit/_files/mview_three.xml | 2 +- .../Mview/Test/Unit/_files/mview_two.xml | 2 +- .../Mview/Test/Unit/_files/valid_mview.xml | 2 +- lib/internal/Magento/Framework/Mview/View.php | 2 +- .../Framework/Mview/View/AbstractFactory.php | 2 +- .../Framework/Mview/View/Changelog.php | 2 +- .../Mview/View/ChangelogInterface.php | 2 +- .../View/ChangelogTableNotExistsException.php | 2 +- .../Framework/Mview/View/Collection.php | 2 +- .../Mview/View/CollectionFactory.php | 2 +- .../Mview/View/CollectionInterface.php | 2 +- .../Mview/View/State/CollectionFactory.php | 2 +- .../Mview/View/State/CollectionInterface.php | 2 +- .../Framework/Mview/View/StateInterface.php | 2 +- .../Framework/Mview/View/Subscription.php | 2 +- .../Mview/View/SubscriptionFactory.php | 2 +- .../Mview/View/SubscriptionInterface.php | 2 +- .../Magento/Framework/Mview/ViewInterface.php | 2 +- .../Magento/Framework/Mview/etc/mview.xsd | 4 +- .../Notification/MessageInterface.php | 2 +- .../Framework/Notification/MessageList.php | 2 +- .../Notification/NotifierInterface.php | 2 +- .../Framework/Notification/NotifierList.php | 2 +- .../Framework/Notification/NotifierPool.php | 2 +- .../Test/Unit/NotifierListTest.php | 2 +- .../Test/Unit/NotifierPoolTest.php | 2 +- .../Framework/Oauth/ConsumerInterface.php | 2 +- .../Magento/Framework/Oauth/Exception.php | 2 +- .../Magento/Framework/Oauth/Helper/Oauth.php | 2 +- .../Framework/Oauth/Helper/Request.php | 2 +- .../Oauth/NonceGeneratorInterface.php | 2 +- .../Magento/Framework/Oauth/Oauth.php | 2 +- .../Framework/Oauth/OauthInputException.php | 2 +- .../Framework/Oauth/OauthInterface.php | 2 +- .../Oauth/Test/Unit/Helper/RequestTest.php | 2 +- .../Test/Unit/OauthInputExceptionTest.php | 2 +- .../Oauth/TokenProviderInterface.php | 2 +- .../Code/Generator/Converter.php | 2 +- .../ObjectManager/Code/Generator/Factory.php | 2 +- .../Code/Generator/Persistor.php | 2 +- .../ObjectManager/Code/Generator/Proxy.php | 2 +- .../Code/Generator/Repository.php | 2 +- .../ObjectManager/Config/Compiled.php | 53 +- .../Framework/ObjectManager/Config/Config.php | 29 +- .../Config/Mapper/ArgumentParser.php | 2 +- .../ObjectManager/Config/Mapper/Dom.php | 2 +- .../ObjectManager/Config/Reader/Dom.php | 2 +- .../Config/Reader/DomFactory.php | 2 +- .../ObjectManager/Config/SchemaLocator.php | 2 +- .../ObjectManager/ConfigCacheInterface.php | 2 +- .../ObjectManager/ConfigInterface.php | 4 +- .../ObjectManager/ConfigLoaderInterface.php | 2 +- .../ObjectManager/ContextInterface.php | 2 +- .../ObjectManager/Definition/Compiled.php | 85 - .../Definition/Compiled/Binary.php | 27 - .../Definition/Compiled/Serialized.php | 27 - .../ObjectManager/Definition/Runtime.php | 2 +- .../ObjectManager/DefinitionFactory.php | 113 +- .../ObjectManager/DefinitionInterface.php | 2 +- .../ObjectManager/DynamicConfigInterface.php | 2 +- .../ObjectManager/Factory/AbstractFactory.php | 62 +- .../ObjectManager/Factory/Compiled.php | 72 +- .../Factory/Dynamic/Developer.php | 41 +- .../Factory/Dynamic/Production.php | 2 +- .../ObjectManager/FactoryInterface.php | 2 +- .../ObjectManager/Helper/Composite.php | 2 +- .../ObjectManager/InterceptableValidator.php | 2 +- .../NoninterceptableInterface.php | 2 +- .../Framework/ObjectManager/ObjectManager.php | 3 +- .../Profiler/Code/Generator/Logger.php | 2 +- .../Profiler/FactoryDecorator.php | 2 +- .../Framework/ObjectManager/Profiler/Log.php | 2 +- .../ObjectManager/Profiler/Tree/Item.php | 2 +- .../ObjectManager/Relations/Compiled.php | 55 - .../ObjectManager/Relations/Runtime.php | 2 +- .../ObjectManager/RelationsInterface.php | 2 +- .../Magento/Framework/ObjectManager/TMap.php | 2 +- .../Framework/ObjectManager/TMapFactory.php | 2 +- .../Unit/Code/Generator/ConverterTest.php | 2 +- .../Test/Unit/Code/Generator/FactoryTest.php | 2 +- .../Code/Generator/GenerateRepositoryTest.php | 2 +- .../Test/Unit/Code/Generator/ProxyTest.php | 2 +- .../Unit/Code/Generator/RepositoryTest.php | 2 +- .../Unit/Code/Generator/_files/Sample.php | 2 +- .../Test/Unit/Config/CompiledTest.php | 164 + .../Test/Unit/Config/ConfigTest.php | 19 +- .../Unit/Config/Mapper/ArgumentParserTest.php | 2 +- .../Test/Unit/Config/Mapper/DomTest.php | 2 +- .../Config/Mapper/_files/argument_parser.xml | 2 +- .../Mapper/_files/mapped_simple_di_config.php | 2 +- .../Config/Mapper/_files/simple_di_config.xml | 2 +- .../Unit/Config/Reader/DomFactoryTest.php | 2 +- .../Test/Unit/Config/Reader/DomTest.php | 2 +- .../Config/Reader/_files/ConfigDomMock.php | 2 +- .../Test/Unit/Config/SchemaLocatorTest.php | 2 +- .../Test/Unit/Config/XsdTest.php | 2 +- .../Config/_files/invalidConfigXmlArray.php | 20 +- .../Test/Unit/Config/_files/valid_config.xml | 7 +- .../Unit/Definition/Compiled/BinaryTest.php | 21 - .../Definition/Compiled/SerializedTest.php | 35 - .../Test/Unit/Definition/CompiledStub.php | 25 - .../Test/Unit/Definition/CompiledTest.php | 37 - .../Test/Unit/DefinitionFactoryTest.php | 115 +- .../Test/Unit/Factory/CompiledTest.php | 180 +- .../Test/Unit/Factory/FactoryTest.php | 2 +- .../Test/Unit/Factory/Fixture/CircularOne.php | 2 +- .../Unit/Factory/Fixture/CircularThree.php | 2 +- .../Test/Unit/Factory/Fixture/CircularTwo.php | 2 +- .../Compiled/DependencySharedTesting.php | 2 +- .../Fixture/Compiled/DependencyTesting.php | 2 +- .../Fixture/Compiled/SimpleClassTesting.php | 2 +- .../Test/Unit/Factory/Fixture/OneScalar.php | 2 +- .../Unit/Factory/Fixture/Polymorphous.php | 2 +- .../Test/Unit/Factory/Fixture/Two.php | 2 +- .../Test/Unit/Helper/CompositeTest.php | 2 +- .../Test/Unit/InterceptableValidatorTest.php | 2 +- .../Test/Unit/ObjectManagerTest.php | 2 +- .../Unit/Profiler/FactoryDecoratorTest.php | 2 +- .../Test/Unit/Relations/CompiledTest.php | 27 - .../Test/Unit/Relations/RuntimeTest.php | 2 +- .../ObjectManager/Test/Unit/TMapTest.php | 2 +- .../_files/Aggregate/AggregateInterface.php | 2 +- .../Unit/_files/Aggregate/AggregateParent.php | 2 +- .../Test/Unit/_files/Aggregate/Child.php | 2 +- .../Unit/_files/Aggregate/WithOptional.php | 2 +- .../ObjectManager/Test/Unit/_files/Child.php | 2 +- .../Test/Unit/_files/Child/A.php | 2 +- .../Test/Unit/_files/Child/Circular.php | 2 +- .../Test/Unit/_files/Child/Interceptor.php | 2 +- .../Test/Unit/_files/Child/Interceptor/A.php | 2 +- .../Test/Unit/_files/Child/Interceptor/B.php | 2 +- .../Test/Unit/_files/ChildInterface.php | 2 +- .../Test/Unit/_files/DiInterface.php | 2 +- .../Test/Unit/_files/DiParent.php | 2 +- .../ObjectManager/Test/Unit/_files/Proxy.php | 2 +- .../Test/Unit/_files/TMap/TClass.php | 2 +- .../Test/Unit/_files/TMap/TInterface.php | 2 +- .../Test/Unit/_files/logger_classes.php | 2 +- .../Framework/ObjectManager/etc/config.xsd | 3 +- .../Framework/ObjectManagerInterface.php | 2 +- .../Framework/Option/ArrayInterface.php | 2 +- .../Magento/Framework/Option/ArrayPool.php | 2 +- lib/internal/Magento/Framework/OsInfo.php | 2 +- lib/internal/Magento/Framework/Parse/Zip.php | 2 +- lib/internal/Magento/Framework/Phrase.php | 2 +- .../Framework/Phrase/Renderer/Composite.php | 2 +- .../Framework/Phrase/Renderer/Inline.php | 2 +- .../Framework/Phrase/Renderer/Placeholder.php | 2 +- .../Framework/Phrase/Renderer/Translate.php | 2 +- .../Framework/Phrase/RendererInterface.php | 2 +- .../Test/Unit/Renderer/CompositeTest.php | 2 +- .../Phrase/Test/Unit/Renderer/InlineTest.php | 2 +- .../Test/Unit/Renderer/PlaceholderTest.php | 2 +- .../Test/Unit/Renderer/TranslateTest.php | 2 +- .../Adjustment/AdjustmentInterface.php | 2 +- .../Pricing/Adjustment/Calculator.php | 2 +- .../Adjustment/CalculatorInterface.php | 2 +- .../Pricing/Adjustment/Collection.php | 2 +- .../Framework/Pricing/Adjustment/Factory.php | 2 +- .../Framework/Pricing/Adjustment/Pool.php | 2 +- .../Pricing/Amount/AmountFactory.php | 2 +- .../Pricing/Amount/AmountInterface.php | 2 +- .../Magento/Framework/Pricing/Amount/Base.php | 2 +- .../Magento/Framework/Pricing/Helper/Data.php | 2 +- .../Framework/Pricing/Price/AbstractPrice.php | 4 +- .../Price/BasePriceProviderInterface.php | 2 +- .../Framework/Pricing/Price/Collection.php | 2 +- .../Framework/Pricing/Price/Factory.php | 2 +- .../Magento/Framework/Pricing/Price/Pool.php | 2 +- .../Pricing/Price/PriceInterface.php | 2 +- .../Framework/Pricing/PriceComposite.php | 2 +- .../Pricing/PriceCurrencyInterface.php | 2 +- .../Framework/Pricing/PriceInfo/Base.php | 2 +- .../Framework/Pricing/PriceInfo/Factory.php | 2 +- .../Framework/Pricing/PriceInfoInterface.php | 2 +- .../Magento/Framework/Pricing/Render.php | 2 +- .../Pricing/Render/AbstractAdjustment.php | 2 +- .../Render/AdjustmentRenderInterface.php | 2 +- .../Framework/Pricing/Render/Amount.php | 2 +- .../Pricing/Render/AmountRenderInterface.php | 2 +- .../Framework/Pricing/Render/Layout.php | 2 +- .../Framework/Pricing/Render/PriceBox.php | 4 +- .../Render/PriceBoxRenderInterface.php | 2 +- .../Framework/Pricing/Render/RendererPool.php | 2 +- .../Framework/Pricing/SaleableInterface.php | 2 +- .../Test/Unit/Adjustment/CalculatorTest.php | 18 +- .../Test/Unit/Adjustment/CollectionTest.php | 2 +- .../Test/Unit/Adjustment/FactoryTest.php | 2 +- .../Pricing/Test/Unit/Adjustment/PoolTest.php | 2 +- .../Test/Unit/Amount/AmountFactoryTest.php | 2 +- .../Pricing/Test/Unit/Amount/BaseTest.php | 2 +- .../Pricing/Test/Unit/Helper/DataTest.php | 2 +- .../Test/Unit/Price/AbstractPriceTest.php | 2 +- .../Test/Unit/Price/CollectionTest.php | 2 +- .../Pricing/Test/Unit/Price/FactoryTest.php | 2 +- .../Pricing/Test/Unit/Price/PoolTest.php | 2 +- .../Pricing/Test/Unit/Price/Stub.php | 2 +- .../Pricing/Test/Unit/PriceInfo/BaseTest.php | 2 +- .../Test/Unit/PriceInfo/FactoryTest.php | 2 +- .../Unit/Render/AbstractAdjustmentTest.php | 2 +- .../Pricing/Test/Unit/Render/AmountTest.php | 2 +- .../Pricing/Test/Unit/Render/LayoutTest.php | 2 +- .../Pricing/Test/Unit/Render/PriceBoxTest.php | 15 +- .../Test/Unit/Render/RendererPoolTest.php | 2 +- .../Pricing/Test/Unit/RenderTest.php | 14 +- .../Process/PhpExecutableFinderFactory.php | 2 +- lib/internal/Magento/Framework/Profiler.php | 2 +- .../Framework/Profiler/Driver/Factory.php | 2 +- .../Framework/Profiler/Driver/Standard.php | 2 +- .../Driver/Standard/AbstractOutput.php | 2 +- .../Driver/Standard/Output/Csvfile.php | 2 +- .../Driver/Standard/Output/Factory.php | 2 +- .../Driver/Standard/Output/Firebug.php | 2 +- .../Profiler/Driver/Standard/Output/Html.php | 2 +- .../Driver/Standard/OutputInterface.php | 2 +- .../Profiler/Driver/Standard/Stat.php | 2 +- .../Framework/Profiler/DriverInterface.php | 2 +- .../Profiler/Test/Unit/Driver/FactoryTest.php | 2 +- .../Driver/Standard/Output/CsvfileTest.php | 2 +- .../Driver/Standard/Output/FactoryTest.php | 2 +- .../Driver/Standard/Output/FirebugTest.php | 2 +- .../Driver/Standard/OutputAbstractTest.php | 2 +- .../Test/Unit/Driver/Standard/StatTest.php | 2 +- .../Test/Unit/Driver/StandardTest.php | 2 +- .../Reflection/AttributeTypeResolver.php | 2 +- .../Reflection/CustomAttributesProcessor.php | 2 +- .../Reflection/DataObjectProcessor.php | 2 +- .../ExtensionAttributesProcessor.php | 2 +- .../Framework/Reflection/FieldNamer.php | 2 +- .../Framework/Reflection/MethodsMap.php | 31 +- .../Framework/Reflection/NameFinder.php | 2 +- .../Test/Unit/AttributeTypeResolverTest.php | 2 +- .../Reflection/Test/Unit/DataObject.php | 2 +- .../Test/Unit/ExtensionAttributesObject.php | 2 +- .../Unit/ExtensionAttributesProcessorTest.php | 2 +- .../Reflection/Test/Unit/FieldNamerTest.php | 2 +- .../Reflection/Test/Unit/MethodsMapTest.php | 101 +- .../Reflection/Test/Unit/NameFinderTest.php | 2 +- .../Reflection/Test/Unit/TypeCasterTest.php | 2 +- .../Test/Unit/TypeProcessorTest.php | 2 +- .../Framework/Reflection/TypeCaster.php | 2 +- .../Framework/Reflection/TypeProcessor.php | 2 +- lib/internal/Magento/Framework/Registry.php | 2 +- .../Magento/Framework/RequireJs/Config.php | 2 +- .../Config/File/Collector/Aggregated.php | 2 +- .../RequireJs/Test/Unit/ConfigTest.php | 2 +- .../Framework/Search/AbstractKeyValuePair.php | 2 +- .../Aggregation/AggregationResolver.php | 2 +- .../AggregationResolverInterface.php | 2 +- .../Search/Adapter/Mysql/Adapter.php | 3 +- .../Adapter/Mysql/Aggregation/Builder.php | 2 +- .../Aggregation/Builder/BucketInterface.php | 2 +- .../Mysql/Aggregation/Builder/Container.php | 2 +- .../Mysql/Aggregation/Builder/Dynamic.php | 2 +- .../Mysql/Aggregation/Builder/Metrics.php | 6 +- .../Mysql/Aggregation/Builder/Range.php | 2 +- .../Mysql/Aggregation/Builder/Term.php | 2 +- .../Aggregation/DataProviderContainer.php | 2 +- .../Aggregation/DataProviderInterface.php | 2 +- .../Adapter/Mysql/Aggregation/Interval.php | 2 +- .../Adapter/Mysql/AggregationFactory.php | 2 +- .../Search/Adapter/Mysql/ConditionManager.php | 2 +- .../Search/Adapter/Mysql/DocumentFactory.php | 2 +- .../Search/Adapter/Mysql/Field/Field.php | 2 +- .../Adapter/Mysql/Field/FieldFactory.php | 2 +- .../Adapter/Mysql/Field/FieldInterface.php | 2 +- .../Search/Adapter/Mysql/Field/Resolver.php | 2 +- .../Adapter/Mysql/Field/ResolverInterface.php | 2 +- .../Search/Adapter/Mysql/Filter/Builder.php | 2 +- .../Mysql/Filter/Builder/FilterInterface.php | 2 +- .../Adapter/Mysql/Filter/Builder/Range.php | 2 +- .../Adapter/Mysql/Filter/Builder/Term.php | 2 +- .../Adapter/Mysql/Filter/Builder/Wildcard.php | 2 +- .../Adapter/Mysql/Filter/BuilderInterface.php | 2 +- .../Adapter/Mysql/Filter/Preprocessor.php | 2 +- .../Mysql/Filter/PreprocessorInterface.php | 2 +- .../Adapter/Mysql/IndexBuilderInterface.php | 2 +- .../Framework/Search/Adapter/Mysql/Mapper.php | 30 +- .../Adapter/Mysql/Query/Builder/Match.php | 2 +- .../Mysql/Query/Builder/QueryInterface.php | 2 +- .../Adapter/Mysql/Query/MatchContainer.php | 2 +- .../Mysql/Query/MatchContainerFactory.php | 2 +- .../Adapter/Mysql/Query/QueryContainer.php | 2 +- .../Mysql/Query/QueryContainerFactory.php | 2 +- .../Search/Adapter/Mysql/ResponseFactory.php | 2 +- .../Search/Adapter/Mysql/ScoreBuilder.php | 2 +- .../Adapter/Mysql/ScoreBuilderFactory.php | 2 +- .../Search/Adapter/Mysql/TemporaryStorage.php | 2 +- .../Adapter/Mysql/TemporaryStorageFactory.php | 2 +- .../Search/Adapter/OptionsInterface.php | 2 +- .../Preprocessor/PreprocessorInterface.php | 2 +- .../Framework/Search/AdapterInterface.php | 2 +- .../Framework/Search/Dynamic/Algorithm.php | 2 +- .../Dynamic/Algorithm/AlgorithmInterface.php | 2 +- .../Search/Dynamic/Algorithm/Auto.php | 2 +- .../Search/Dynamic/Algorithm/Improved.php | 2 +- .../Search/Dynamic/Algorithm/Manual.php | 2 +- .../Search/Dynamic/Algorithm/Repository.php | 2 +- .../Search/Dynamic/DataProviderFactory.php | 2 +- .../Search/Dynamic/DataProviderInterface.php | 2 +- .../Search/Dynamic/EntityStorage.php | 2 +- .../Search/Dynamic/EntityStorageFactory.php | 2 +- .../Search/Dynamic/IntervalFactory.php | 2 +- .../Search/Dynamic/IntervalInterface.php | 2 +- .../Framework/Search/EntityMetadata.php | 2 +- .../Magento/Framework/Search/Request.php | 2 +- .../Request/Aggregation/DynamicBucket.php | 2 +- .../Search/Request/Aggregation/Metric.php | 2 +- .../Search/Request/Aggregation/Range.php | 2 +- .../Request/Aggregation/RangeBucket.php | 2 +- .../Search/Request/Aggregation/Status.php | 2 +- .../Request/Aggregation/StatusInterface.php | 2 +- .../Search/Request/Aggregation/TermBucket.php | 2 +- .../Framework/Search/Request/Binder.php | 2 +- .../Search/Request/BucketInterface.php | 2 +- .../Framework/Search/Request/Builder.php | 2 +- .../Framework/Search/Request/Cleaner.php | 2 +- .../Framework/Search/Request/Config.php | 21 +- .../Search/Request/Config/Converter.php | 2 +- .../Request/Config/FilesystemReader.php | 2 +- .../Search/Request/Config/SchemaLocator.php | 2 +- .../Framework/Search/Request/Dimension.php | 2 +- .../Request/EmptyRequestDataException.php | 2 +- .../Search/Request/Filter/BoolExpression.php | 2 +- .../Framework/Search/Request/Filter/Range.php | 2 +- .../Framework/Search/Request/Filter/Term.php | 2 +- .../Search/Request/Filter/Wildcard.php | 2 +- .../Search/Request/FilterInterface.php | 2 +- .../Request/IndexScopeResolverInterface.php | 2 +- .../Framework/Search/Request/Mapper.php | 2 +- .../NonExistingRequestNameException.php | 2 +- .../Search/Request/Query/BoolExpression.php | 2 +- .../Framework/Search/Request/Query/Filter.php | 2 +- .../Framework/Search/Request/Query/Match.php | 2 +- .../Search/Request/QueryInterface.php | 2 +- .../Framework/Search/RequestInterface.php | 2 +- .../Framework/Search/Response/Aggregation.php | 2 +- .../Search/Response/Aggregation/Value.php | 2 +- .../Framework/Search/Response/Bucket.php | 2 +- .../Search/Response/QueryResponse.php | 2 +- .../Framework/Search/ResponseInterface.php | 2 +- .../Magento/Framework/Search/Search.php | 2 +- .../Search/SearchEngine/Config/Converter.php | 2 +- .../Search/SearchEngine/Config/Reader.php | 2 +- .../SearchEngine/Config/SchemaLocator.php | 2 +- .../Search/SearchEngine/ConfigInterface.php | 2 +- .../Search/SearchEngineInterface.php | 2 +- .../Search/SearchResponseBuilder.php | 2 +- .../Aggregation/AggregationResolverTest.php | 2 +- .../Test/Unit/Adapter/Mysql/AdapterTest.php | 2 +- .../Aggregation/Builder/ContainerTest.php | 2 +- .../Mysql/Aggregation/Builder/MetricsTest.php | 2 +- .../Mysql/Aggregation/Builder/RangeTest.php | 2 +- .../Mysql/Aggregation/Builder/TermTest.php | 2 +- .../Adapter/Mysql/Aggregation/BuilderTest.php | 2 +- .../Aggregation/DataProviderContainerTest.php | 2 +- .../Adapter/Mysql/ConditionManagerTest.php | 2 +- .../Mysql/Filter/Builder/RangeTest.php | 2 +- .../Adapter/Mysql/Filter/Builder/TermTest.php | 2 +- .../Mysql/Filter/Builder/WildcardTest.php | 2 +- .../Unit/Adapter/Mysql/Filter/BuilderTest.php | 2 +- .../Test/Unit/Adapter/Mysql/MapperTest.php | 20 +- .../Adapter/Mysql/Query/Builder/MatchTest.php | 2 +- .../Mysql/Query/QueryContainerTest.php | 2 +- .../Adapter/Mysql/ResponseFactoryTest.php | 2 +- .../Unit/Adapter/Mysql/ScoreBuilderTest.php | 2 +- .../Adapter/Mysql/TemporaryStorageTest.php | 2 +- .../Test/Unit/Dynamic/IntervalFactoryTest.php | 2 +- .../Unit/Request/Aggregation/StatusTest.php | 2 +- .../Search/Test/Unit/Request/BinderTest.php | 2 +- .../Search/Test/Unit/Request/BuilderTest.php | 2 +- .../Search/Test/Unit/Request/CleanerTest.php | 2 +- .../Unit/Request/Config/SchemaLocatorTest.php | 2 +- .../Search/Test/Unit/Request/MapperTest.php | 2 +- .../Test/Unit/Response/AggregationTest.php | 2 +- .../Test/Unit/Response/QueryResponseTest.php | 2 +- .../SearchEngine/Config/ConverterTest.php | 2 +- .../SearchEngine/Config/SchemaLocatorTest.php | 2 +- .../Test/Unit/SearchResponseBuilderTest.php | 2 +- .../Framework/Search/Test/Unit/SearchTest.php | 2 +- .../Search/Test/Unit/_files/search_engine.xml | 2 +- .../Magento/Framework/Search/etc/requests.xsd | 3 +- .../Framework/Search/etc/search_engine.xsd | 2 +- .../Framework/Search/etc/search_request.xsd | 4 +- .../Search/etc/search_request_merged.xsd | 4 +- .../Magento/Framework/Serialize/README.md | 8 + .../Framework/Serialize/Serializer/Json.php | 30 + .../Serialize/Serializer/Serialize.php | 45 + .../Serialize/SerializerInterface.php | 28 + .../Test/Unit/Serializer/JsonTest.php | 78 + .../Test/Unit/Serializer/SerializeTest.php | 71 + .../Magento/Framework/Session/Config.php | 2 +- .../Session/Config/ConfigInterface.php | 2 +- .../Validator/CookieDomainValidator.php | 2 +- .../Validator/CookieLifetimeValidator.php | 2 +- .../Config/Validator/CookiePathValidator.php | 2 +- .../Magento/Framework/Session/Generic.php | 2 +- .../Magento/Framework/Session/SaveHandler.php | 2 +- .../Framework/Session/SaveHandler/DbTable.php | 2 +- .../Framework/Session/SaveHandler/Native.php | 2 +- .../Framework/Session/SaveHandler/Redis.php | 2 +- .../Session/SaveHandler/Redis/Config.php | 2 +- .../Session/SaveHandler/Redis/Logger.php | 2 +- .../Framework/Session/SaveHandlerFactory.php | 2 +- .../Session/SaveHandlerInterface.php | 2 +- .../Framework/Session/SessionManager.php | 3 +- .../Session/SessionManagerInterface.php | 2 +- .../Magento/Framework/Session/SidResolver.php | 2 +- .../Session/SidResolverInterface.php | 2 +- .../Magento/Framework/Session/Storage.php | 2 +- .../Framework/Session/StorageInterface.php | 2 +- .../Session/Test/Unit/ConfigTest.php | 2 +- .../Test/Unit/SaveHandler/DbTableTest.php | 2 +- .../Unit/SaveHandler/Redis/ConfigTest.php | 2 +- .../Unit/SaveHandler/Redis/LoggerTest.php | 2 +- .../Test/Unit/SaveHandlerFactoryTest.php | 2 +- .../Session/Test/Unit/SessionManagerTest.php | 2 +- .../Session/Test/Unit/_files/mock_ini_set.php | 2 +- .../_files/mock_session_regenerate_id.php | 4 +- .../Magento/Framework/Session/Validator.php | 2 +- .../Framework/Session/ValidatorInterface.php | 2 +- .../Setup/BackendFrontnameGenerator.php | 2 +- .../Framework/Setup/BackupRollback.php | 2 +- .../Framework/Setup/BackupRollbackFactory.php | 2 +- .../Setup/ConfigOptionsListInterface.php | 2 +- .../Magento/Framework/Setup/ConsoleLogger.php | 2 +- .../Framework/Setup/DataCacheInterface.php | 2 +- .../Framework/Setup/ExternalFKSetup.php | 2 +- .../Framework/Setup/FilePermissions.php | 12 +- .../Framework/Setup/InstallDataInterface.php | 2 +- .../Setup/InstallSchemaInterface.php | 2 +- .../Magento/Framework/Setup/Lists.php | 2 +- .../Framework/Setup/LoggerInterface.php | 2 +- .../Setup/ModuleContextInterface.php | 2 +- .../Setup/ModuleDataSetupInterface.php | 2 +- .../Setup/Option/AbstractConfigOption.php | 2 +- .../Setup/Option/FlagConfigOption.php | 2 +- .../Setup/Option/MultiSelectConfigOption.php | 2 +- .../Setup/Option/SelectConfigOption.php | 2 +- .../Setup/Option/TextConfigOption.php | 2 +- .../Framework/Setup/SampleData/Context.php | 2 +- .../Framework/Setup/SampleData/Executor.php | 2 +- .../Setup/SampleData/FixtureManager.php | 2 +- .../Setup/SampleData/InstallerInterface.php | 2 +- .../Framework/Setup/SampleData/State.php | 2 +- .../Setup/SampleData/StateInterface.php | 2 +- .../Framework/Setup/SchemaSetupInterface.php | 2 +- .../Framework/Setup/SetupInterface.php | 2 +- .../Unit/BackendFrontnameGeneratorTest.php | 2 +- .../Test/Unit/BackupRollbackFactoryTest.php | 2 +- .../Setup/Test/Unit/BackupRollbackTest.php | 2 +- .../Setup/Test/Unit/ConsoleLoggerTest.php | 2 +- .../Setup/Test/Unit/FilePermissionsTest.php | 9 +- .../Framework/Setup/Test/Unit/ListsTest.php | 2 +- .../Test/Unit/Option/FlagConfigOptionTest.php | 2 +- .../Option/MultiSelectConfigOptionTest.php | 2 +- .../Unit/Option/SelectConfigOptionTest.php | 2 +- .../Test/Unit/Option/TextConfigOptionTest.php | 2 +- .../Setup/Test/Unit/SampleData/StateTest.php | 2 +- .../Framework/Setup/UninstallInterface.php | 2 +- .../Framework/Setup/UpgradeDataInterface.php | 2 +- .../Setup/UpgradeSchemaInterface.php | 2 +- lib/internal/Magento/Framework/Shell.php | 2 +- .../Framework/Shell/CommandRenderer.php | 2 +- .../Shell/CommandRendererBackground.php | 2 +- .../Shell/CommandRendererInterface.php | 2 +- .../Framework/Shell/ComplexParameter.php | 2 +- .../Magento/Framework/Shell/Driver.php | 2 +- .../Magento/Framework/Shell/Response.php | 2 +- .../Unit/CommandRendererBackgroundTest.php | 2 +- .../Shell/Test/Unit/CommandRendererTest.php | 2 +- .../Shell/Test/Unit/ComplexParameterTest.php | 2 +- .../Magento/Framework/ShellInterface.php | 2 +- .../Magento/Framework/Simplexml/Config.php | 2 +- .../Simplexml/Config/Cache/AbstractCache.php | 2 +- .../Framework/Simplexml/Config/Cache/File.php | 2 +- .../Magento/Framework/Simplexml/Element.php | 2 +- .../Unit/Config/Cache/AbstractCacheTest.php | 2 +- .../Simplexml/Test/Unit/ConfigTest.php | 2 +- .../Simplexml/Test/Unit/ElementTest.php | 2 +- .../Simplexml/Test/Unit/_files/data.xml | 4 +- .../Test/Unit/_files/extend_data.xml | 2 +- .../Simplexml/Test/Unit/_files/mixed_data.xml | 2 +- .../Magento/Framework/Stdlib/ArrayManager.php | 24 +- .../Magento/Framework/Stdlib/ArrayUtils.php | 2 +- .../Magento/Framework/Stdlib/BooleanUtils.php | 2 +- .../Stdlib/Cookie/CookieMetadata.php | 2 +- .../Stdlib/Cookie/CookieMetadataFactory.php | 2 +- .../Stdlib/Cookie/CookieReaderInterface.php | 2 +- .../Framework/Stdlib/Cookie/CookieScope.php | 2 +- .../Stdlib/Cookie/CookieScopeInterface.php | 2 +- .../CookieSizeLimitReachedException.php | 2 +- .../Stdlib/Cookie/FailureToSendException.php | 2 +- .../Stdlib/Cookie/PhpCookieManager.php | 2 +- .../Stdlib/Cookie/PhpCookieReader.php | 2 +- .../Stdlib/Cookie/PublicCookieMetadata.php | 2 +- .../Stdlib/Cookie/SensitiveCookieMetadata.php | 2 +- .../Stdlib/CookieManagerInterface.php | 2 +- .../Magento/Framework/Stdlib/DateTime.php | 2 +- .../Framework/Stdlib/DateTime/DateTime.php | 2 +- .../Stdlib/DateTime/DateTimeFormatter.php | 2 +- .../DateTime/DateTimeFormatterInterface.php | 2 +- .../Framework/Stdlib/DateTime/Filter/Date.php | 2 +- .../Stdlib/DateTime/Filter/DateTime.php | 2 +- .../Framework/Stdlib/DateTime/Timezone.php | 2 +- .../Stdlib/DateTime/Timezone/Validator.php | 2 +- .../Stdlib/DateTime/TimezoneInterface.php | 2 +- .../Magento/Framework/Stdlib/StringUtils.php | 2 +- .../Stdlib/Test/Unit/ArrayManagerTest.php | 38 +- .../Stdlib/Test/Unit/ArrayUtilsTest.php | 2 +- .../Stdlib/Test/Unit/BooleanUtilsTest.php | 2 +- .../Test/Unit/Cookie/CookieScopeTest.php | 2 +- .../Test/Unit/Cookie/PhpCookieManagerTest.php | 2 +- .../Unit/Cookie/PublicCookieMetadataTest.php | 2 +- .../Cookie/SensitiveCookieMetadataTest.php | 2 +- .../Unit/Cookie/_files/setcookie_mock.php | 2 +- .../Unit/DateTime/DateTimeFormatterTest.php | 2 +- .../Test/Unit/DateTime/DateTimeTest.php | 2 +- .../Test/Unit/DateTime/Filter/DateTest.php | 2 +- .../Unit/DateTime/Filter/DateTimeTest.php | 2 +- .../Unit/DateTime/Timezone/ValidatorTest.php | 2 +- .../Stdlib/Test/Unit/StringUtilsTest.php | 2 +- .../Stdlib/Test/Unit/_files/gmdate_mock.php | 2 +- .../Magento/Framework/System/Dirs.php | 2 +- lib/internal/Magento/Framework/System/Ftp.php | 2 +- .../Test/Unit/App/ResourceConnectionTest.php | 2 +- .../Test/Unit/App/Scope/SourceTest.php | 2 +- .../Framework/Test/Unit/ArchiveTest.php | 2 +- .../Framework/Test/Unit/AuthorizationTest.php | 2 +- .../Framework/Test/Unit/CurrencyTest.php | 2 +- .../Test/Unit/DB/Query/BatchIteratorTest.php | 2 +- .../Unit/DB/Query/BatchRangeIteratorTest.php | 121 + .../Test/Unit/DB/Query/GeneratorTest.php | 47 +- .../Framework/Test/Unit/EscaperTest.php | 2 +- .../Framework/Test/Unit/EventFactoryTest.php | 2 +- .../Magento/Framework/Test/Unit/EventTest.php | 2 +- .../Framework/Test/Unit/FilesystemTest.php | 2 +- .../Magento/Framework/Test/Unit/FlagTest.php | 37 +- .../Unit/Interception/InterceptorTest.php | 2 +- .../Test/Unit/Interception/Sample/Entity.php | 2 +- .../Unit/Interception/Sample/Interceptor.php | 2 +- .../Test/Unit/Interception/Sample/Plugin1.php | 2 +- .../Test/Unit/Interception/Sample/Plugin2.php | 2 +- .../Test/Unit/Interception/Sample/Plugin3.php | 2 +- .../Test/Unit/Interception/Sample/Plugin4.php | 2 +- .../Test/Unit/Message/PhraseFactoryTest.php | 2 +- .../Module/Plugin/DbStatusValidatorTest.php | 2 +- .../Framework/Test/Unit/ObjectTest.php | 2 +- .../Framework/Test/Unit/PhraseTest.php | 2 +- .../Framework/Test/Unit/ProfilerTest.php | 2 +- .../Framework/Test/Unit/RegistryTest.php | 2 +- .../Magento/Framework/Test/Unit/ShellTest.php | 2 +- .../Test/Unit/Translate/Js/ConfigTest.php | 2 +- .../Framework/Test/Unit/TranslateTest.php | 23 +- .../Magento/Framework/Test/Unit/UrlTest.php | 23 +- .../Magento/Framework/Test/Unit/UtilTest.php | 2 +- .../Test/Unit/ValidatorFactoryTest.php | 2 +- .../Framework/Test/Unit/ValidatorTest.php | 2 +- .../View/Design/Theme/Label/OptionsTest.php | 2 +- .../Unit/Unit/Helper/ProxyTestingTest.php | 2 +- .../Unit/Matcher/MethodInvokedAtIndexTest.php | 2 +- .../Unit/Unit/Utility/XsdValidatorTest.php | 2 +- .../Test/Unit/Unit/Utility/_files/invalid.xml | 2 +- .../Test/Unit/Unit/Utility/_files/valid.xml | 2 +- .../Test/Unit/Unit/Utility/_files/valid.xsd | 2 +- .../Unit/AbstractFactoryTestCase.php | 2 +- .../ExtensionGeneratorAutoloader.php | 2 +- .../TestFramework/Unit/BaseTestCase.php | 2 +- .../TestFramework/Unit/Block/Adminhtml.php | 2 +- .../Unit/Helper/ObjectManager.php | 7 +- .../Unit/Helper/ProxyTesting.php | 2 +- .../Unit/Helper/SelectRendererTrait.php | 2 +- .../Unit/Listener/GarbageCleanup.php | 2 +- .../Unit/Matcher/MethodInvokedAtIndex.php | 2 +- .../TestFramework/Unit/Module/Config.php | 2 +- .../Unit/Utility/XsdValidator.php | 2 +- lib/internal/Magento/Framework/Translate.php | 26 +- .../Framework/Translate/AbstractAdapter.php | 2 +- .../Magento/Framework/Translate/Adapter.php | 2 +- .../Framework/Translate/AdapterInterface.php | 2 +- .../Magento/Framework/Translate/Inline.php | 2 +- .../Translate/Inline/ConfigInterface.php | 2 +- .../Translate/Inline/ParserFactory.php | 2 +- .../Translate/Inline/ParserInterface.php | 2 +- .../Framework/Translate/Inline/Provider.php | 2 +- .../Translate/Inline/ProviderInterface.php | 2 +- .../Framework/Translate/Inline/Proxy.php | 2 +- .../Framework/Translate/Inline/State.php | 2 +- .../Translate/Inline/StateInterface.php | 2 +- .../Framework/Translate/InlineInterface.php | 2 +- .../Magento/Framework/Translate/Js/Config.php | 2 +- .../Translate/Locale/Resolver/Plugin.php | 2 +- .../Framework/Translate/ResourceInterface.php | 2 +- .../Test/Unit/AdapterAbstractTest.php | 2 +- .../Translate/Test/Unit/AdapterTest.php | 2 +- .../Translate/Test/Unit/Inline/ProxyTest.php | 2 +- .../Translate/Test/Unit/Inline/StateTest.php | 2 +- .../Translate/Test/Unit/InlineTest.php | 2 +- .../Magento/Framework/TranslateInterface.php | 2 +- .../Magento/Framework/Unserialize/README.md | 3 +- .../Unserialize/Test/Unit/UnserializeTest.php | 2 +- .../Framework/Unserialize/Unserialize.php | 5 +- lib/internal/Magento/Framework/Url.php | 29 +- .../Magento/Framework/Url/Decoder.php | 2 +- .../Framework/Url/DecoderInterface.php | 2 +- .../Magento/Framework/Url/Encoder.php | 2 +- .../Framework/Url/EncoderInterface.php | 2 +- .../Magento/Framework/Url/Helper/Data.php | 2 +- .../Magento/Framework/Url/HostChecker.php | 48 + .../Framework/Url/ModifierComposite.php | 2 +- .../Framework/Url/ModifierInterface.php | 2 +- .../Framework/Url/QueryParamsResolver.php | 2 +- .../Url/QueryParamsResolverInterface.php | 2 +- .../Url/RouteParamsPreprocessorComposite.php | 2 +- .../Url/RouteParamsPreprocessorInterface.php | 2 +- .../Framework/Url/RouteParamsResolver.php | 2 +- .../Url/RouteParamsResolverFactory.php | 2 +- .../Url/RouteParamsResolverInterface.php | 2 +- .../Magento/Framework/Url/ScopeInterface.php | 2 +- .../Magento/Framework/Url/ScopeResolver.php | 2 +- .../Framework/Url/ScopeResolverInterface.php | 2 +- .../Magento/Framework/Url/SecurityInfo.php | 2 +- .../Framework/Url/SecurityInfoInterface.php | 2 +- .../Framework/Url/Test/Unit/DecoderTest.php | 2 +- .../Url/Test/Unit/Helper/DataTest.php | 2 +- .../Url/Test/Unit/HostCheckerTest.php | 63 + .../Url/Test/Unit/QueryParamsResolverTest.php | 2 +- .../Unit/RouteParamsResolverFactoryTest.php | 2 +- .../Url/Test/Unit/ScopeResolverTest.php | 2 +- .../Url/Test/Unit/SecurityInfoTest.php | 2 +- .../Framework/Url/Test/Unit/ValidatorTest.php | 2 +- .../Magento/Framework/Url/Validator.php | 2 +- lib/internal/Magento/Framework/UrlFactory.php | 2 +- .../Magento/Framework/UrlInterface.php | 2 +- lib/internal/Magento/Framework/Util.php | 2 +- lib/internal/Magento/Framework/Validator.php | 2 +- .../Framework/Validator/AbstractValidator.php | 2 +- .../Framework/Validator/AllowedProtocols.php | 59 + .../Magento/Framework/Validator/Alnum.php | 2 +- .../Magento/Framework/Validator/Builder.php | 2 +- .../Magento/Framework/Validator/Config.php | 2 +- .../Framework/Validator/Constraint.php | 2 +- .../Framework/Validator/Constraint/Option.php | 2 +- .../Validator/Constraint/Option/Callback.php | 2 +- .../Validator/Constraint/OptionInterface.php | 2 +- .../Validator/Constraint/Property.php | 2 +- .../Framework/Validator/ConstraintFactory.php | 2 +- .../Magento/Framework/Validator/Currency.php | 2 +- .../Framework/Validator/DataObject.php | 2 +- .../Framework/Validator/EmailAddress.php | 2 +- .../Framework/Validator/Entity/Properties.php | 2 +- .../Magento/Framework/Validator/Exception.php | 2 +- .../Magento/Framework/Validator/Factory.php | 60 +- .../Framework/Validator/File/Extension.php | 2 +- .../Framework/Validator/File/ImageSize.php | 2 +- .../Framework/Validator/File/IsImage.php | 2 +- .../Magento/Framework/Validator/File/Size.php | 2 +- .../Framework/Validator/FloatUtils.php | 2 +- .../Magento/Framework/Validator/IntUtils.php | 2 +- .../Magento/Framework/Validator/Ip.php | 2 +- .../Magento/Framework/Validator/Locale.php | 2 +- .../Magento/Framework/Validator/NotEmpty.php | 2 +- .../Magento/Framework/Validator/Regex.php | 2 +- .../Framework/Validator/StringLength.php | 2 +- .../Validator/Test/Unit/BuilderTest.php | 2 +- .../Validator/Test/Unit/ConfigTest.php | 2 +- .../Unit/Constraint/Option/CallbackTest.php | 2 +- .../Test/Unit/Constraint/OptionTest.php | 2 +- .../Test/Unit/Constraint/PropertyTest.php | 2 +- .../Validator/Test/Unit/ConstraintTest.php | 2 +- .../Validator/Test/Unit/CurrencyTest.php | 2 +- .../Test/Unit/Entity/PropertiesTest.php | 2 +- .../Validator/Test/Unit/ExceptionTest.php | 2 +- .../Validator/Test/Unit/FactoryTest.php | 293 +- .../Validator/Test/Unit/LocaleTest.php | 2 +- .../Validator/Test/Unit/ObjectTest.php | 2 +- .../Validator/Test/Unit/StringLengthTest.php | 2 +- .../Validator/Test/Unit/Test/Alnum.php | 2 +- .../Validator/Test/Unit/Test/Callback.php | 2 +- .../Validator/Test/Unit/Test/IsInt.php | 2 +- .../Validator/Test/Unit/Test/IsTrue.php | 2 +- .../Validator/Test/Unit/Test/NotEmpty.php | 2 +- .../Validator/Test/Unit/Test/StringLength.php | 2 +- .../Validator/Test/Unit/TimezoneTest.php | 2 +- .../Framework/Validator/Test/Unit/UrlTest.php | 76 + .../Test/Unit/ValidatorAbstractTest.php | 2 +- .../negative/invalid_builder_class.xml | 2 +- .../negative/invalid_builder_instance.xml | 2 +- .../negative/invalid_child_for_option.xml | 2 +- .../negative/invalid_constraint.xml | 2 +- .../negative/invalid_content_for_callback.xml | 2 +- .../negative/invalid_entity_callback.xml | 2 +- .../validation/negative/invalid_method.xml | 2 +- .../negative/invalid_method_callback.xml | 2 +- .../multiple_callback_in_argument.xml | 2 +- .../negative/no_class_for_constraint.xml | 2 +- .../validation/negative/no_constraint.xml | 2 +- .../negative/no_name_for_entity.xml | 2 +- .../validation/negative/no_name_for_group.xml | 2 +- .../validation/negative/no_name_for_rule.xml | 2 +- .../negative/no_rule_for_reference.xml | 2 +- .../validation/negative/not_unique_use.xml | 2 +- .../positive/builder/validation.xml | 2 +- .../positive/module_a/validation.xml | 2 +- .../positive/module_b/validation.xml | 2 +- .../Magento/Framework/Validator/Timezone.php | 2 +- .../Framework/Validator/UniversalFactory.php | 2 +- .../Magento/Framework/Validator/Url.php | 37 + .../Validator/ValidatorInterface.php | 2 +- .../Framework/Validator/etc/validation.xsd | 2 +- .../Magento/Framework/ValidatorFactory.php | 2 +- .../Framework/View/Asset/AssetInterface.php | 2 +- .../Magento/Framework/View/Asset/Bundle.php | 2 +- .../Framework/View/Asset/Bundle/Config.php | 2 +- .../View/Asset/Bundle/ConfigInterface.php | 2 +- .../Framework/View/Asset/Bundle/Manager.php | 2 +- .../Framework/View/Asset/Collection.php | 2 +- .../Magento/Framework/View/Asset/Config.php | 2 +- .../Framework/View/Asset/ConfigInterface.php | 2 +- .../View/Asset/ContentProcessorException.php | 2 +- .../View/Asset/ContentProcessorInterface.php | 2 +- .../Framework/View/Asset/ContextInterface.php | 2 +- .../Magento/Framework/View/Asset/File.php | 2 +- .../Framework/View/Asset/File/Context.php | 2 +- .../View/Asset/File/ContextFactory.php | 2 +- .../View/Asset/File/FallbackContext.php | 18 +- .../Asset/File/FallbackContextFactory.php | 2 +- .../View/Asset/File/NotFoundException.php | 2 +- .../Framework/View/Asset/FileFactory.php | 2 +- .../View/Asset/GroupedCollection.php | 2 +- .../Framework/View/Asset/LocalInterface.php | 2 +- .../Framework/View/Asset/LockerProcess.php | 2 +- .../View/Asset/LockerProcessInterface.php | 2 +- .../Framework/View/Asset/MergeService.php | 2 +- .../View/Asset/MergeStrategy/Checksum.php | 27 +- .../View/Asset/MergeStrategy/Direct.php | 9 +- .../View/Asset/MergeStrategy/FileExists.php | 2 +- .../View/Asset/MergeStrategyInterface.php | 2 +- .../View/Asset/MergeableInterface.php | 2 +- .../Magento/Framework/View/Asset/Merged.php | 2 +- .../Framework/View/Asset/Minification.php | 2 +- .../View/Asset/NotationResolver/Module.php | 2 +- .../View/Asset/NotationResolver/Variable.php | 2 +- .../Asset/PreProcessor/AlternativeSource.php | 2 +- .../AlternativeSource/AssetBuilder.php | 2 +- .../AlternativeSourceInterface.php | 2 +- .../View/Asset/PreProcessor/Chain.php | 2 +- .../View/Asset/PreProcessor/ChainFactory.php | 2 +- .../PreProcessor/ChainFactoryInterface.php | 2 +- .../FilenameResolverInterface.php | 2 +- .../View/Asset/PreProcessor/Helper/Sort.php | 2 +- .../PreProcessor/Helper/SortInterface.php | 2 +- .../MinificationFilenameResolver.php | 2 +- .../View/Asset/PreProcessor/Minify.php | 2 +- .../Asset/PreProcessor/ModuleNotation.php | 2 +- .../View/Asset/PreProcessor/Passthrough.php | 2 +- .../View/Asset/PreProcessor/Pool.php | 2 +- .../Asset/PreProcessor/VariableNotation.php | 2 +- .../View/Asset/PreProcessorInterface.php | 2 +- .../Framework/View/Asset/PropertyGroup.php | 2 +- .../View/Asset/PropertyGroupFactory.php | 2 +- .../Magento/Framework/View/Asset/Remote.php | 2 +- .../Framework/View/Asset/RemoteFactory.php | 2 +- .../Framework/View/Asset/Repository.php | 5 +- .../Magento/Framework/View/Asset/Source.php | 2 +- .../Asset/SourceFileGeneratorInterface.php | 2 +- .../View/Asset/SourceFileGeneratorPool.php | 2 +- .../Magento/Framework/View/BlockPool.php | 2 +- .../Magento/Framework/View/Config.php | 4 +- .../Framework/View/ConfigInterface.php | 2 +- .../Magento/Framework/View/Context.php | 2 +- .../Magento/Framework/View/DataSourcePool.php | 2 +- .../View/Design/Fallback/Rule/Composite.php | 2 +- .../Design/Fallback/Rule/ModularSwitch.php | 2 +- .../Fallback/Rule/ModularSwitchFactory.php | 2 +- .../View/Design/Fallback/Rule/Module.php | 2 +- .../Design/Fallback/Rule/ModuleFactory.php | 2 +- .../Design/Fallback/Rule/RuleInterface.php | 2 +- .../View/Design/Fallback/Rule/Simple.php | 2 +- .../Design/Fallback/Rule/SimpleFactory.php | 2 +- .../View/Design/Fallback/Rule/Theme.php | 2 +- .../Design/Fallback/Rule/ThemeFactory.php | 2 +- .../View/Design/Fallback/RulePool.php | 2 +- .../Fallback/EmailTemplateFile.php | 2 +- .../Design/FileResolution/Fallback/File.php | 2 +- .../FileResolution/Fallback/LocaleFile.php | 2 +- .../Fallback/Resolver/Alternative.php | 2 +- .../Fallback/Resolver/Minification.php | 2 +- .../Fallback/Resolver/Simple.php | 2 +- .../Fallback/ResolverInterface.php | 2 +- .../FileResolution/Fallback/StaticFile.php | 2 +- .../FileResolution/Fallback/TemplateFile.php | 2 +- .../View/Design/Theme/Customization.php | 2 +- .../Theme/Customization/AbstractFile.php | 2 +- .../Theme/Customization/ConfigInterface.php | 2 +- .../Design/Theme/Customization/File/Css.php | 2 +- .../Design/Theme/Customization/File/Js.php | 2 +- .../Customization/FileAssetInterface.php | 2 +- .../Theme/Customization/FileInterface.php | 2 +- .../Customization/FileServiceFactory.php | 2 +- .../View/Design/Theme/Customization/Path.php | 2 +- .../Design/Theme/CustomizationInterface.php | 2 +- .../View/Design/Theme/Domain/Factory.php | 2 +- .../Design/Theme/Domain/PhysicalInterface.php | 2 +- .../Design/Theme/Domain/StagingInterface.php | 2 +- .../Design/Theme/Domain/VirtualInterface.php | 2 +- .../Design/Theme/File/CollectionInterface.php | 2 +- .../View/Design/Theme/FileFactory.php | 2 +- .../View/Design/Theme/FileInterface.php | 2 +- .../Design/Theme/FileProviderInterface.php | 2 +- .../View/Design/Theme/FlyweightFactory.php | 18 +- .../Framework/View/Design/Theme/Image.php | 2 +- .../View/Design/Theme/Image/PathInterface.php | 2 +- .../View/Design/Theme/Image/Uploader.php | 2 +- .../View/Design/Theme/ImageFactory.php | 2 +- .../Framework/View/Design/Theme/Label.php | 2 +- .../View/Design/Theme/Label/ListInterface.php | 2 +- .../View/Design/Theme/Label/Options.php | 2 +- .../View/Design/Theme/LabelFactory.php | 2 +- .../View/Design/Theme/ListInterface.php | 2 +- .../View/Design/Theme/ResolverInterface.php | 2 +- .../View/Design/Theme/ThemePackage.php | 2 +- .../View/Design/Theme/ThemePackageFactory.php | 2 +- .../View/Design/Theme/ThemePackageList.php | 2 +- .../Design/Theme/ThemeProviderInterface.php | 2 +- .../Framework/View/Design/Theme/Validator.php | 2 +- .../Framework/View/Design/ThemeFactory.php | 2 +- .../Framework/View/Design/ThemeInterface.php | 2 +- .../Framework/View/DesignExceptions.php | 19 +- .../Framework/View/DesignInterface.php | 2 +- .../Magento/Framework/View/DesignLoader.php | 2 +- .../Framework/View/Element/AbstractBlock.php | 21 +- .../View/Element/Block/ArgumentInterface.php | 14 + .../Framework/View/Element/BlockFactory.php | 2 +- .../Framework/View/Element/BlockInterface.php | 2 +- .../Framework/View/Element/Context.php | 2 +- .../View/Element/ExceptionHandlerBlock.php | 2 +- .../Element/ExceptionHandlerBlockFactory.php | 2 +- .../Framework/View/Element/FormKey.php | 2 +- .../Framework/View/Element/Html/Calendar.php | 2 +- .../Framework/View/Element/Html/Date.php | 2 +- .../Framework/View/Element/Html/Link.php | 2 +- .../View/Element/Html/Link/Current.php | 2 +- .../Framework/View/Element/Html/Links.php | 2 +- .../Framework/View/Element/Html/Select.php | 2 +- .../Framework/View/Element/Js/Components.php | 2 +- .../Framework/View/Element/Js/Cookie.php | 2 +- .../Message/InterpretationMediator.php | 2 +- .../Message/InterpretationStrategy.php | 2 +- .../InterpretationStrategyInterface.php | 2 +- .../Message/MessageConfigurationsPool.php | 2 +- .../Message/Renderer/BlockRenderer.php | 2 +- .../Renderer/BlockRenderer/Template.php | 2 +- .../Message/Renderer/EscapeRenderer.php | 2 +- .../Message/Renderer/PoolInterface.php | 2 +- .../Message/Renderer/RendererInterface.php | 2 +- .../Message/Renderer/RenderersPool.php | 2 +- .../Framework/View/Element/Messages.php | 2 +- .../Framework/View/Element/Redirect.php | 2 +- .../View/Element/RendererInterface.php | 2 +- .../Framework/View/Element/RendererList.php | 2 +- .../Framework/View/Element/Template.php | 4 +- .../View/Element/Template/Context.php | 2 +- .../View/Element/Template/File/Resolver.php | 2 +- .../View/Element/Template/File/Validator.php | 2 +- .../Magento/Framework/View/Element/Text.php | 2 +- .../Framework/View/Element/Text/ListText.php | 2 +- .../View/Element/Text/TextList/Item.php | 2 +- .../View/Element/Text/TextList/Link.php | 2 +- .../Interpreter/ConfigurableObject.php | 2 +- .../UiComponent/ArrayObjectFactory.php | 2 +- .../UiComponent/BlockWrapperInterface.php | 2 +- .../Element/UiComponent/Config/Converter.php | 2 +- .../Element/UiComponent/Config/DomMerger.php | 2 +- .../UiComponent/Config/DomMergerInterface.php | 2 +- .../FileCollector/AggregatedFileCollector.php | 2 +- .../AggregatedFileCollectorFactory.php | 2 +- .../Config/FileCollectorInterface.php | 2 +- .../UiComponent/Config/ManagerInterface.php | 2 +- .../Config/Provider/Component/Definition.php | 26 +- .../UiComponent/Config/Provider/Template.php | 49 +- .../Element/UiComponent/Config/Reader.php | 2 +- .../UiComponent/Config/ReaderFactory.php | 2 +- .../UiComponent/Config/UiReaderInterface.php | 2 +- .../UiComponent/ContainerInterface.php | 2 +- .../ContentType/AbstractContentType.php | 2 +- .../ContentType/ContentTypeFactory.php | 2 +- .../ContentType/ContentTypeInterface.php | 2 +- .../Element/UiComponent/ContentType/Html.php | 2 +- .../Element/UiComponent/ContentType/Json.php | 2 +- .../Element/UiComponent/ContentType/Xml.php | 2 +- .../View/Element/UiComponent/Context.php | 2 +- .../Element/UiComponent/ContextFactory.php | 2 +- .../Element/UiComponent/ContextInterface.php | 2 +- .../UiComponent/Control/ActionPoolFactory.php | 2 +- .../Control/ActionPoolInterface.php | 2 +- .../Control/ButtonProviderFactory.php | 2 +- .../Control/ButtonProviderInterface.php | 2 +- .../UiComponent/Control/ControlInterface.php | 2 +- .../UiComponent/Control/DummyButton.php | 2 +- .../DataProvider/CollectionFactory.php | 2 +- .../UiComponent/DataProvider/DataProvider.php | 2 +- .../DataProvider/DataProviderInterface.php | 2 +- .../UiComponent/DataProvider/Document.php | 2 +- .../DataProvider/FilterApplierInterface.php | 2 +- .../UiComponent/DataProvider/FilterPool.php | 2 +- .../DataProvider/FulltextFilter.php | 2 +- .../DataProvider/RegularFilter.php | 2 +- .../UiComponent/DataProvider/Reporting.php | 2 +- .../UiComponent/DataProvider/SearchResult.php | 2 +- .../UiComponent/DataSourceInterface.php | 2 +- .../Element/UiComponent/JsConfigInterface.php | 2 +- .../Element/UiComponent/LayoutInterface.php | 2 +- .../Element/UiComponent/ObserverInterface.php | 2 +- .../Element/UiComponent/PoolInterface.php | 2 +- .../View/Element/UiComponent/Processor.php | 2 +- .../Element/UiComponent/SubjectInterface.php | 2 +- .../View/Element/UiComponentFactory.php | 2 +- .../View/Element/UiComponentInterface.php | 2 +- .../View/EntitySpecificHandlesList.php | 40 + lib/internal/Magento/Framework/View/File.php | 2 +- .../Framework/View/File/Collector/Base.php | 2 +- .../Collector/Decorator/ModuleDependency.php | 2 +- .../File/Collector/Decorator/ModuleOutput.php | 2 +- .../View/File/Collector/Override/Base.php | 2 +- .../File/Collector/Override/ThemeModular.php | 2 +- .../Framework/View/File/Collector/Theme.php | 2 +- .../View/File/Collector/ThemeModular.php | 2 +- .../View/File/CollectorInterface.php | 2 +- .../Magento/Framework/View/File/Factory.php | 2 +- .../Magento/Framework/View/File/FileList.php | 2 +- .../View/File/FileList/CollateInterface.php | 2 +- .../Framework/View/File/FileList/Collator.php | 2 +- .../Framework/View/File/FileList/Factory.php | 2 +- .../Magento/Framework/View/FileSystem.php | 2 +- .../Magento/Framework/View/Helper/Js.php | 2 +- .../Framework/View/Helper/PathPattern.php | 2 +- .../Magento/Framework/View/Layout.php | 25 +- .../Argument/Interpreter/DataObject.php | 2 +- .../Interpreter/Decorator/Updater.php | 2 +- .../Argument/Interpreter/HelperMethod.php | 2 +- .../Argument/Interpreter/NamedParams.php | 2 +- .../Layout/Argument/Interpreter/Options.php | 2 +- .../Argument/Interpreter/Passthrough.php | 2 +- .../View/Layout/Argument/Interpreter/Url.php | 2 +- .../Framework/View/Layout/Argument/Parser.php | 2 +- .../View/Layout/Argument/UpdaterInterface.php | 2 +- .../Magento/Framework/View/Layout/Builder.php | 2 +- .../Framework/View/Layout/BuilderFactory.php | 2 +- .../View/Layout/BuilderInterface.php | 2 +- .../Framework/View/Layout/Data/Structure.php | 2 +- .../Magento/Framework/View/Layout/Element.php | 2 +- .../View/Layout/File/Collector/Aggregated.php | 2 +- .../Framework/View/Layout/Generator/Block.php | 7 +- .../View/Layout/Generator/Container.php | 2 +- .../View/Layout/Generator/Context.php | 2 +- .../View/Layout/Generator/ContextFactory.php | 2 +- .../View/Layout/Generator/Structure.php | 2 +- .../View/Layout/Generator/UiComponent.php | 2 +- .../View/Layout/GeneratorInterface.php | 2 +- .../Framework/View/Layout/GeneratorPool.php | 2 +- .../Magento/Framework/View/Layout/Generic.php | 2 +- .../Framework/View/Layout/PageType/Config.php | 2 +- .../View/Layout/PageType/Config/Converter.php | 2 +- .../View/Layout/PageType/Config/Reader.php | 2 +- .../Layout/PageType/Config/SchemaLocator.php | 2 +- .../Magento/Framework/View/Layout/Pool.php | 2 +- .../View/Layout/ProcessorFactory.php | 2 +- .../View/Layout/ProcessorInterface.php | 2 +- .../Magento/Framework/View/Layout/Proxy.php | 2 +- .../Framework/View/Layout/Reader/Block.php | 16 +- .../View/Layout/Reader/Container.php | 9 +- .../Framework/View/Layout/Reader/Context.php | 2 +- .../View/Layout/Reader/ContextFactory.php | 2 +- .../Framework/View/Layout/Reader/Move.php | 2 +- .../View/Layout/Reader/UiComponent.php | 4 +- .../Framework/View/Layout/ReaderFactory.php | 2 +- .../Framework/View/Layout/ReaderInterface.php | 2 +- .../Framework/View/Layout/ReaderPool.php | 2 +- .../View/Layout/ScheduledStructure.php | 69 +- .../View/Layout/ScheduledStructure/Helper.php | 2 +- .../Framework/View/Layout/etc/body.xsd | 2 +- .../Framework/View/Layout/etc/elements.xsd | 3 +- .../Framework/View/Layout/etc/head.xsd | 2 +- .../Framework/View/Layout/etc/html.xsd | 2 +- .../View/Layout/etc/layout_generic.xsd | 2 +- .../View/Layout/etc/layout_merged.xsd | 2 +- .../View/Layout/etc/page_configuration.xsd | 2 +- .../Framework/View/Layout/etc/page_layout.xsd | 2 +- .../Framework/View/Layout/etc/page_types.xsd | 2 +- .../Magento/Framework/View/LayoutFactory.php | 2 +- .../Framework/View/LayoutInterface.php | 2 +- .../Framework/View/Model/Layout/Merge.php | 27 +- .../View/Model/Layout/Translator.php | 2 +- .../View/Model/Layout/Update/Validator.php | 2 +- .../PageLayout/Config/BuilderInterface.php | 2 +- .../Magento/Framework/View/Page/Builder.php | 2 +- .../Magento/Framework/View/Page/Config.php | 2 +- .../View/Page/Config/Generator/Body.php | 2 +- .../View/Page/Config/Generator/Head.php | 2 +- .../View/Page/Config/Reader/Body.php | 2 +- .../View/Page/Config/Reader/Head.php | 2 +- .../View/Page/Config/Reader/Html.php | 2 +- .../Framework/View/Page/Config/Renderer.php | 2 +- .../View/Page/Config/RendererFactory.php | 2 +- .../View/Page/Config/RendererInterface.php | 2 +- .../Framework/View/Page/Config/Structure.php | 58 +- .../Framework/View/Page/ConfigFactory.php | 2 +- .../Framework/View/Page/FaviconInterface.php | 2 +- .../Framework/View/Page/Layout/Reader.php | 2 +- .../Magento/Framework/View/Page/Title.php | 2 +- .../Framework/View/PageLayout/Config.php | 2 +- .../PageLayout/File/Collector/Aggregated.php | 2 +- .../Framework/View/PageLayout/etc/layouts.xsd | 2 +- .../Framework/View/Render/RenderFactory.php | 2 +- .../Framework/View/RenderInterface.php | 2 +- .../Magento/Framework/View/Result/Layout.php | 2 +- .../Framework/View/Result/LayoutFactory.php | 2 +- .../Magento/Framework/View/Result/Page.php | 25 +- .../Framework/View/Result/PageFactory.php | 2 +- .../Framework/View/Template/Html/Minifier.php | 2 +- .../View/Template/Html/MinifierInterface.php | 2 +- .../Framework/View/TemplateEngine/Php.php | 2 +- .../Framework/View/TemplateEngine/Xhtml.php | 2 +- .../View/TemplateEngine/Xhtml/Compiler.php | 2 +- .../Xhtml/Compiler/Attribute.php | 2 +- .../Xhtml/Compiler/AttributeInterface.php | 2 +- .../TemplateEngine/Xhtml/Compiler/Cdata.php | 2 +- .../Xhtml/Compiler/CdataInterface.php | 2 +- .../TemplateEngine/Xhtml/Compiler/Comment.php | 2 +- .../Xhtml/Compiler/CommentInterface.php | 2 +- .../Compiler/Directive/CallableMethod.php | 2 +- .../Compiler/Directive/DirectiveInterface.php | 2 +- .../Xhtml/Compiler/Directive/Variable.php | 2 +- .../Compiler/Element/ElementInterface.php | 2 +- .../TemplateEngine/Xhtml/Compiler/Text.php | 2 +- .../Xhtml/Compiler/TextInterface.php | 2 +- .../TemplateEngine/Xhtml/CompilerFactory.php | 2 +- .../Xhtml/CompilerInterface.php | 2 +- .../TemplateEngine/Xhtml/ResultFactory.php | 2 +- .../TemplateEngine/Xhtml/ResultInterface.php | 2 +- .../View/TemplateEngine/Xhtml/Template.php | 2 +- .../TemplateEngine/Xhtml/TemplateFactory.php | 2 +- .../Framework/View/TemplateEngineFactory.php | 2 +- .../View/TemplateEngineInterface.php | 2 +- .../Framework/View/TemplateEnginePool.php | 2 +- .../Test/Unit/Asset/Bundle/ManagerTest.php | 2 +- .../View/Test/Unit/Asset/BundleTest.php | 2 +- .../View/Test/Unit/Asset/CollectionTest.php | 2 +- .../View/Test/Unit/Asset/ConfigTest.php | 2 +- .../Unit/Asset/File/FallbackContextTest.php | 11 +- .../View/Test/Unit/Asset/FileTest.php | 2 +- .../Test/Unit/Asset/GroupedCollectionTest.php | 2 +- .../Test/Unit/Asset/LockerProcessTest.php | 2 +- .../View/Test/Unit/Asset/MergeServiceTest.php | 2 +- .../Unit/Asset/MergeStrategy/ChecksumTest.php | 29 +- .../Unit/Asset/MergeStrategy/DirectTest.php | 33 +- .../Asset/MergeStrategy/FileExistsTest.php | 2 +- .../View/Test/Unit/Asset/MergedTest.php | 2 +- .../View/Test/Unit/Asset/MinificationTest.php | 2 +- .../Asset/NotationResolver/ModuleTest.php | 2 +- .../Asset/NotationResolver/VariableTest.php | 2 +- .../PreProcessor/AlternativeSourceTest.php | 2 +- .../Unit/Asset/PreProcessor/ChainTest.php | 2 +- .../Asset/PreProcessor/Helper/SortTest.php | 2 +- .../MinificationFilenameResolverTest.php | 2 +- .../Unit/Asset/PreProcessor/MinifyTest.php | 2 +- .../Test/Unit/Asset/PreProcessor/PoolTest.php | 2 +- .../Test/Unit/Asset/PropertyGroupTest.php | 2 +- .../View/Test/Unit/Asset/RemoteTest.php | 2 +- .../View/Test/Unit/Asset/RepositoryTest.php | 8 +- .../View/Test/Unit/Asset/SourceTest.php | 2 +- .../View/Test/Unit/BlockPoolTest.php | 2 +- .../View/Test/Unit/BlockPoolTestBlock.php | 2 +- .../Framework/View/Test/Unit/ConfigTest.php | 8 +- .../Framework/View/Test/Unit/ContextTest.php | 2 +- .../View/Test/Unit/DataSourcePoolTest.php | 2 +- .../Test/Unit/DataSourcePoolTestBlock.php | 2 +- .../Design/Fallback/Rule/CompositeTest.php | 2 +- .../Fallback/Rule/ModularSwitchTest.php | 2 +- .../Unit/Design/Fallback/Rule/ModuleTest.php | 2 +- .../Unit/Design/Fallback/Rule/SimpleTest.php | 2 +- .../Unit/Design/Fallback/Rule/ThemeTest.php | 2 +- .../Unit/Design/Fallback/RulePoolTest.php | 2 +- .../FileResolution/Fallback/FileTest.php | 2 +- .../Fallback/LocaleFileTest.php | 2 +- .../Fallback/Resolver/AlternativeTest.php | 2 +- .../Fallback/Resolver/MinificationTest.php | 2 +- .../Fallback/Resolver/SimpleTest.php | 2 +- .../Fallback/StaticFileTest.php | 2 +- .../Fallback/TemplateFileTest.php | 2 +- .../Theme/Customization/AbstractFileTest.php | 2 +- .../Design/Theme/Customization/PathTest.php | 2 +- .../Unit/Design/Theme/CustomizationTest.php | 2 +- .../Unit/Design/Theme/Domain/FactoryTest.php | 2 +- .../Design/Theme/FlyweightFactoryTest.php | 11 +- .../Unit/Design/Theme/Image/UploaderTest.php | 2 +- .../View/Test/Unit/Design/Theme/ImageTest.php | 2 +- .../View/Test/Unit/Design/Theme/LabelTest.php | 2 +- .../Design/Theme/ThemePackageListTest.php | 2 +- .../Unit/Design/Theme/ThemePackageTest.php | 2 +- .../View/Test/Unit/DesignExceptionsTest.php | 46 +- .../View/Test/Unit/DesignLoaderTest.php | 2 +- .../Test/Unit/Element/AbstractBlockTest.php | 35 +- .../Test/Unit/Element/BlockFactoryTest.php | 2 +- .../View/Test/Unit/Element/FormKeyTest.php | 2 +- .../Test/Unit/Element/Html/CalendarTest.php | 2 +- .../Unit/Element/Html/Link/CurrentTest.php | 2 +- .../View/Test/Unit/Element/Html/LinkTest.php | 2 +- .../View/Test/Unit/Element/Html/LinksTest.php | 2 +- .../Test/Unit/Element/Html/SelectTest.php | 2 +- .../View/Test/Unit/Element/Js/CookieTest.php | 2 +- .../Message/InterpretationMediatorTest.php | 2 +- .../Message/InterpretationStrategyTest.php | 2 +- .../Message/MessageConfigurationsPoolTest.php | 2 +- .../Renderer/BlockRenderer/TemplateTest.php | 2 +- .../Message/Renderer/BlockRendererTest.php | 2 +- .../Message/Renderer/EscapeRendererTest.php | 2 +- .../Message/Renderer/RenderersPoolTest.php | 2 +- .../View/Test/Unit/Element/MessagesTest.php | 2 +- .../Test/Unit/Element/RendererListTest.php | 2 +- .../Element/Template/File/ResolverTest.php | 2 +- .../Element/Template/File/ValidatorTest.php | 2 +- .../View/Test/Unit/Element/TemplateTest.php | 2 +- .../Unit/Element/Text/TextList/ItemTest.php | 2 +- .../Unit/Element/Text/TextList/LinkTest.php | 2 +- .../View/Test/Unit/Element/TextTest.php | 2 +- .../Unit/Element/UiComponent/ContextTest.php | 2 +- .../UiComponent/Control/DummyButtonTest.php | 2 +- .../DataProvider/FulltextFilterTest.php | 2 +- .../Element/UiComponent/ProcessorTest.php | 2 +- .../Unit/EntitySpecificHandlesListTest.php | 31 + .../Test/Unit/File/Collector/BaseTest.php | 2 +- .../Decorator/ModuleDependencyTest.php | 2 +- .../Collector/Decorator/ModuleOutputTest.php | 2 +- .../Unit/File/Collector/Override/BaseTest.php | 2 +- .../Collector/Override/ThemeModularTest.php | 2 +- .../Unit/File/Collector/ThemeModularTest.php | 2 +- .../Test/Unit/File/Collector/ThemeTest.php | 2 +- .../View/Test/Unit/File/FactoryTest.php | 2 +- .../Test/Unit/File/FileList/CollatorTest.php | 2 +- .../Test/Unit/File/FileList/FactoryTest.php | 2 +- .../View/Test/Unit/File/FileListTest.php | 2 +- .../View/Test/Unit/FileSystemTest.php | 2 +- .../Framework/View/Test/Unit/FileTest.php | 2 +- .../View/Test/Unit/Helper/JsTest.php | 2 +- .../View/Test/Unit/Helper/PathPatternTest.php | 2 +- .../Interpreter/Decorator/UpdaterTest.php | 2 +- .../Argument/Interpreter/HelperMethodTest.php | 2 +- .../Argument/Interpreter/NamedParamsTest.php | 2 +- .../Argument/Interpreter/ObjectTest.php | 30 +- .../Argument/Interpreter/OptionsTest.php | 2 +- .../Argument/Interpreter/PassthroughTest.php | 2 +- .../Layout/Argument/Interpreter/UrlTest.php | 2 +- .../Test/Unit/Layout/Argument/ParserTest.php | 2 +- .../Unit/Layout/Argument/_files/arguments.xml | 2 +- .../Test/Unit/Layout/BuilderFactoryTest.php | 2 +- .../View/Test/Unit/Layout/BuilderTest.php | 2 +- .../Test/Unit/Layout/Data/StructureTest.php | 2 +- .../View/Test/Unit/Layout/ElementTest.php | 2 +- .../Layout/File/Collector/AggregateTest.php | 2 +- .../Test/Unit/Layout/Generator/BlockTest.php | 2 +- .../Unit/Layout/Generator/ContainerTest.php | 2 +- .../Unit/Layout/Generator/UiComponentTest.php | 2 +- .../Test/Unit/Layout/GeneratorPoolTest.php | 2 +- .../Test/Unit/Layout/Reader/BlockTest.php | 8 +- .../Test/Unit/Layout/Reader/ContainerTest.php | 8 +- .../Test/Unit/Layout/Reader/FactoryTest.php | 2 +- .../View/Test/Unit/Layout/Reader/MoveTest.php | 2 +- .../Unit/Layout/Reader/UiComponentTest.php | 11 +- .../View/Test/Unit/Layout/ReaderPoolTest.php | 2 +- .../Layout/ScheduledStructure/HelperTest.php | 2 +- .../Unit/Layout/ScheduledStructureTest.php | 2 +- .../View/Test/Unit/Layout/XsdTest.php | 2 +- .../View/Test/Unit/Layout/_files/action.xml | 4 +- .../Test/Unit/Layout/_files/arguments.xml | 4 +- .../_files/invalidLayoutArgumentsXmlArray.php | 2 +- .../View/Test/Unit/LayoutFactoryTest.php | 2 +- .../Framework/View/Test/Unit/LayoutTest.php | 101 +- .../View/Test/Unit/Model/Layout/MergeTest.php | 2 +- .../Test/Unit/Model/Layout/TranslatorTest.php | 2 +- .../Model/Layout/Update/ValidatorTest.php | 2 +- .../View/Test/Unit/Page/BuilderTest.php | 2 +- .../Unit/Page/Config/Generator/BodyTest.php | 2 +- .../Unit/Page/Config/Generator/HeadTest.php | 2 +- .../Test/Unit/Page/Config/Reader/HeadTest.php | 2 +- .../Test/Unit/Page/Config/RendererTest.php | 2 +- .../Test/Unit/Page/Config/StructureTest.php | 2 +- .../Unit/Page/Config/_files/template_body.xml | 2 +- .../Unit/Page/Config/_files/template_head.xml | 2 +- .../Unit/Page/Config/_files/template_html.xml | 2 +- .../View/Test/Unit/Page/ConfigTest.php | 2 +- .../View/Test/Unit/Page/Layout/ReaderTest.php | 2 +- .../View/Test/Unit/Page/TitleTest.php | 2 +- .../View/Test/Unit/PageLayout/ConfigTest.php | 2 +- .../Unit/PageLayout/_files/layouts_one.xml | 2 +- .../Unit/PageLayout/_files/layouts_two.xml | 2 +- .../Test/Unit/Render/RenderFactoryTest.php | 2 +- .../View/Test/Unit/Result/LayoutTest.php | 2 +- .../View/Test/Unit/Result/PageFactoryTest.php | 2 +- .../View/Test/Unit/Result/PageTest.php | 63 +- .../Test/Unit/Template/Html/MinifierTest.php | 6 +- .../View/Test/Unit/TemplateEngine/PhpTest.php | 2 +- .../Unit/TemplateEngine/_files/simple.phtml | 2 +- .../Test/Unit/TemplateEngineFactoryTest.php | 2 +- .../View/Test/Unit/TemplateEnginePoolTest.php | 2 +- .../View/Test/Unit/Url/ConfigTest.php | 2 +- .../View/Test/Unit/Url/CssResolverTest.php | 2 +- .../View/Test/Unit/Url/_files/result.css | 2 +- .../Test/Unit/Url/_files/resultImport.css | 2 +- .../Test/Unit/Url/_files/resultNormalized.css | 2 +- .../View/Test/Unit/Url/_files/source.css | 2 +- .../Test/Unit/Url/_files/sourceImport.css | 2 +- .../Magento/Framework/View/Url/Config.php | 2 +- .../Framework/View/Url/ConfigInterface.php | 2 +- .../Framework/View/Url/CssResolver.php | 2 +- .../Xsd/Media/TypeDataExtractorInterface.php | 2 +- .../View/Xsd/Media/TypeDataExtractorPool.php | 2 +- .../Framework/Webapi/Authorization.php | 2 +- .../CustomAttributeTypeLocatorInterface.php | 2 +- .../Framework/Webapi/ErrorProcessor.php | 2 +- .../Magento/Framework/Webapi/Exception.php | 2 +- .../Magento/Framework/Webapi/Request.php | 2 +- .../Magento/Framework/Webapi/Response.php | 2 +- .../Magento/Framework/Webapi/Rest/Request.php | 2 +- .../Webapi/Rest/Request/Deserializer/Json.php | 2 +- .../Webapi/Rest/Request/Deserializer/Xml.php | 2 +- .../Rest/Request/DeserializerFactory.php | 2 +- .../Rest/Request/DeserializerInterface.php | 2 +- .../Rest/Request/ParamOverriderInterface.php | 2 +- .../Framework/Webapi/Rest/Response.php | 2 +- .../Webapi/Rest/Response/FieldsFilter.php | 2 +- .../Webapi/Rest/Response/Renderer/Json.php | 2 +- .../Webapi/Rest/Response/Renderer/Xml.php | 2 +- .../Webapi/Rest/Response/RendererFactory.php | 2 +- .../Rest/Response/RendererInterface.php | 2 +- .../Webapi/ServiceInputProcessor.php | 2 +- .../Webapi/ServiceOutputProcessor.php | 2 +- .../ServicePayloadConverterInterface.php | 2 +- .../Framework/Webapi/Soap/ClientFactory.php | 2 +- .../Webapi/Test/Unit/ErrorProcessorTest.php | 2 +- .../Webapi/Test/Unit/RequestTest.php | 2 +- .../Webapi/Test/Unit/ResponseTest.php | 2 +- .../Rest/Request/Deserializer/JsonTest.php | 2 +- .../Rest/Request/Deserializer/XmlTest.php | 2 +- .../Rest/Request/DeserializerFactoryTest.php | 2 +- .../Webapi/Test/Unit/Rest/RequestTest.php | 2 +- .../Unit/Rest/Response/FieldsFilterTest.php | 2 +- .../Unit/Rest/Response/Renderer/JsonTest.php | 2 +- .../Unit/Rest/Response/Renderer/XmlTest.php | 2 +- .../Rest/Response/RendererFactoryTest.php | 2 +- .../Webapi/Test/Unit/Rest/ResponseTest.php | 2 +- .../AssociativeArray.php | 2 +- .../Unit/ServiceInputProcessor/DataArray.php | 2 +- .../Unit/ServiceInputProcessor/Nested.php | 2 +- .../ObjectWithCustomAttributes.php | 2 +- .../Unit/ServiceInputProcessor/Simple.php | 2 +- .../ServiceInputProcessor/SimpleArray.php | 2 +- .../ServiceInputProcessor/TestService.php | 2 +- .../Test/Unit/ServiceInputProcessorTest.php | 22 +- .../Magento/Framework/Xml/Generator.php | 2 +- lib/internal/Magento/Framework/Xml/Parser.php | 7 +- .../Magento/Framework/Xml/Security.php | 2 +- .../Framework/Xml/Test/Unit/ParserTest.php | 2 +- .../Framework/Xml/Test/Unit/SecurityTest.php | 2 +- .../Framework/Xml/Test/Unit/_files/data.xml | 4 +- .../XsltProcessor/XsltProcessorFactory.php | 2 +- .../Magento/Framework/ZendEscaper.php | 2 +- lib/internal/Magento/Framework/composer.json | 6 +- .../Magento/Framework/registration.php | 2 +- lib/web/css/docs/actions-toolbar.html | 2 +- lib/web/css/docs/breadcrumbs.html | 2 +- lib/web/css/docs/buttons.html | 2 +- lib/web/css/docs/components.html | 2 +- lib/web/css/docs/docs.css | 4 +- lib/web/css/docs/docs.html | 2 +- lib/web/css/docs/dropdowns.html | 2 +- lib/web/css/docs/forms.html | 2 +- lib/web/css/docs/icons.html | 2 +- lib/web/css/docs/index.html | 2 +- lib/web/css/docs/layout.html | 2 +- lib/web/css/docs/lib.html | 2 +- lib/web/css/docs/loaders.html | 2 +- lib/web/css/docs/messages.html | 2 +- lib/web/css/docs/pages.html | 2 +- lib/web/css/docs/popups.html | 2 +- lib/web/css/docs/rating.html | 2 +- lib/web/css/docs/resets.html | 2 +- lib/web/css/docs/responsive.html | 2 +- lib/web/css/docs/sections.html | 2 +- lib/web/css/docs/source/_actions-toolbar.less | 2 +- lib/web/css/docs/source/_breadcrumbs.less | 2 +- lib/web/css/docs/source/_buttons.less | 2 +- lib/web/css/docs/source/_components.less | 2 +- lib/web/css/docs/source/_dropdowns.less | 2 +- lib/web/css/docs/source/_forms.less | 2 +- lib/web/css/docs/source/_icons.less | 2 +- lib/web/css/docs/source/_layout.less | 2 +- lib/web/css/docs/source/_lib.less | 2 +- lib/web/css/docs/source/_loaders.less | 2 +- lib/web/css/docs/source/_messages.less | 2 +- lib/web/css/docs/source/_pages.less | 2 +- lib/web/css/docs/source/_popups.less | 2 +- lib/web/css/docs/source/_rating.less | 2 +- lib/web/css/docs/source/_resets.less | 2 +- lib/web/css/docs/source/_responsive.less | 2 +- lib/web/css/docs/source/_sections.less | 2 +- lib/web/css/docs/source/_tables.less | 2 +- lib/web/css/docs/source/_tooltips.less | 2 +- lib/web/css/docs/source/_typography.less | 2 +- lib/web/css/docs/source/_utilities.less | 2 +- lib/web/css/docs/source/_variables.less | 2 +- lib/web/css/docs/source/docs.less | 2 +- lib/web/css/docs/source/js/dropdown.js | 4 +- lib/web/css/docs/tables.html | 2 +- lib/web/css/docs/tooltips.html | 2 +- lib/web/css/docs/typography.html | 2 +- lib/web/css/docs/utilities.html | 2 +- lib/web/css/docs/variables.html | 2 +- lib/web/css/source/_email-variables.less | 4 +- lib/web/css/source/_extend.less | 2 +- lib/web/css/source/_theme.less | 2 +- lib/web/css/source/_variables.less | 2 +- lib/web/css/source/_widgets.less | 2 +- lib/web/css/source/components/_modals.less | 6 +- lib/web/css/source/lib/_actions-toolbar.less | 2 +- lib/web/css/source/lib/_breadcrumbs.less | 2 +- lib/web/css/source/lib/_buttons.less | 2 +- lib/web/css/source/lib/_dropdowns.less | 2 +- lib/web/css/source/lib/_forms.less | 2 +- lib/web/css/source/lib/_grids.less | 2 +- lib/web/css/source/lib/_icons.less | 3 +- lib/web/css/source/lib/_layout.less | 2 +- lib/web/css/source/lib/_lib.less | 2 +- lib/web/css/source/lib/_loaders.less | 2 +- lib/web/css/source/lib/_messages.less | 2 +- lib/web/css/source/lib/_navigation.less | 2 +- lib/web/css/source/lib/_pages.less | 2 +- lib/web/css/source/lib/_popups.less | 2 +- lib/web/css/source/lib/_rating.less | 2 +- lib/web/css/source/lib/_resets.less | 2 +- lib/web/css/source/lib/_responsive.less | 2 +- lib/web/css/source/lib/_sections.less | 2 +- lib/web/css/source/lib/_tables.less | 2 +- lib/web/css/source/lib/_tooltips.less | 2 +- lib/web/css/source/lib/_typography.less | 2 +- lib/web/css/source/lib/_utilities.less | 2 +- lib/web/css/source/lib/_variables.less | 2 +- .../lib/variables/_actions-toolbar.less | 2 +- .../source/lib/variables/_breadcrumbs.less | 2 +- .../css/source/lib/variables/_buttons.less | 2 +- lib/web/css/source/lib/variables/_colors.less | 2 +- .../css/source/lib/variables/_components.less | 2 +- .../css/source/lib/variables/_dropdowns.less | 2 +- lib/web/css/source/lib/variables/_email.less | 2 +- lib/web/css/source/lib/variables/_forms.less | 2 +- lib/web/css/source/lib/variables/_icons.less | 2 +- lib/web/css/source/lib/variables/_layout.less | 2 +- .../css/source/lib/variables/_loaders.less | 2 +- .../css/source/lib/variables/_messages.less | 2 +- .../css/source/lib/variables/_navigation.less | 2 +- lib/web/css/source/lib/variables/_pages.less | 2 +- lib/web/css/source/lib/variables/_popups.less | 2 +- lib/web/css/source/lib/variables/_rating.less | 2 +- .../css/source/lib/variables/_responsive.less | 2 +- .../css/source/lib/variables/_sections.less | 2 +- .../css/source/lib/variables/_structure.less | 2 +- lib/web/css/source/lib/variables/_tables.less | 2 +- .../css/source/lib/variables/_tooltips.less | 2 +- .../css/source/lib/variables/_typography.less | 2 +- lib/web/extjs/defaults.js | 2 +- lib/web/legacy-build.min.js | 2 +- lib/web/less/config.less.js | 2 +- lib/web/mage/accordion.js | 73 +- lib/web/mage/adminhtml/accordion.js | 146 +- lib/web/mage/adminhtml/backup.js | 204 +- lib/web/mage/adminhtml/browser.js | 283 +- lib/web/mage/adminhtml/events.js | 119 +- lib/web/mage/adminhtml/form.js | 835 +- lib/web/mage/adminhtml/globals.js | 11 +- lib/web/mage/adminhtml/grid.js | 2363 ++++-- lib/web/mage/adminhtml/tools.js | 5 +- lib/web/mage/adminhtml/varienLoader.js | 2 +- .../wysiwyg/tiny_mce/html5-schema.js | 4 +- .../plugins/magentovariable/editor_plugin.js | 47 +- .../plugins/magentowidget/editor_plugin.js | 82 +- .../mage/adminhtml/wysiwyg/tiny_mce/setup.js | 375 +- .../themes/advanced/skins/default/content.css | 2 +- .../themes/advanced/skins/default/dialog.css | 2 +- lib/web/mage/adminhtml/wysiwyg/widget.js | 541 +- lib/web/mage/app/config.js | 17 +- lib/web/mage/apply/main.js | 15 +- lib/web/mage/apply/scripts.js | 7 +- lib/web/mage/backend/action-link.js | 54 +- lib/web/mage/backend/bootstrap.js | 61 +- lib/web/mage/backend/button.js | 4 +- lib/web/mage/backend/editablemultiselect.js | 257 +- lib/web/mage/backend/floating-header.js | 40 +- lib/web/mage/backend/form.js | 100 +- lib/web/mage/backend/jstree-mixin.js | 2 +- lib/web/mage/backend/menu.js | 449 +- lib/web/mage/backend/notification.js | 35 +- lib/web/mage/backend/suggest.js | 157 +- lib/web/mage/backend/tabs.js | 205 +- lib/web/mage/backend/tree-suggest.js | 56 +- lib/web/mage/backend/validation.js | 114 +- lib/web/mage/bootstrap.js | 3 +- lib/web/mage/calendar.css | 2 +- lib/web/mage/calendar.js | 6 +- lib/web/mage/captcha.js | 46 +- lib/web/mage/collapsible.js | 310 +- lib/web/mage/common.js | 4 +- lib/web/mage/cookies.js | 9 +- lib/web/mage/dataPost.js | 86 +- lib/web/mage/decorate.js | 59 +- lib/web/mage/deletable-item.js | 20 +- lib/web/mage/dialog.js | 13 +- lib/web/mage/dropdown.js | 162 +- lib/web/mage/dropdown_old.js | 31 +- lib/web/mage/dropdowns.js | 87 +- lib/web/mage/edit-trigger.js | 99 +- lib/web/mage/fieldset-controls.js | 70 +- lib/web/mage/gallery/gallery.html | 2 +- lib/web/mage/gallery/gallery.js | 32 +- lib/web/mage/gallery/gallery.less | 2 +- lib/web/mage/gallery/module/_extends.less | 2 +- lib/web/mage/gallery/module/_focus.less | 2 +- lib/web/mage/gallery/module/_fullscreen.less | 4 +- lib/web/mage/gallery/module/_mixins.less | 4 +- lib/web/mage/gallery/module/_variables.less | 2 +- lib/web/mage/ie-class-fixer.js | 5 +- lib/web/mage/item-table.js | 8 +- lib/web/mage/layout.js | 20 +- lib/web/mage/list.js | 93 +- lib/web/mage/loader.js | 35 +- lib/web/mage/loader_old.js | 42 +- lib/web/mage/mage.js | 7 +- lib/web/mage/menu.js | 321 +- lib/web/mage/popup-window.js | 46 +- lib/web/mage/redirect-url.js | 23 +- lib/web/mage/requirejs/mixins.js | 2 +- lib/web/mage/requirejs/resolver.js | 2 +- lib/web/mage/requirejs/static.js | 2 +- lib/web/mage/requirejs/text.js | 2 +- lib/web/mage/smart-keyboard-handler.js | 60 +- lib/web/mage/sticky.js | 42 +- lib/web/mage/storage.js | 10 +- lib/web/mage/tabs.js | 243 +- lib/web/mage/template.js | 3 +- lib/web/mage/terms.js | 167 +- lib/web/mage/toggle.js | 69 +- lib/web/mage/tooltip.js | 17 +- lib/web/mage/translate-inline-vde.css | 2 +- lib/web/mage/translate-inline-vde.js | 276 +- lib/web/mage/translate-inline.css | 2 +- lib/web/mage/translate-inline.js | 121 +- lib/web/mage/translate.js | 25 +- lib/web/mage/url.js | 21 +- lib/web/mage/utils/arrays.js | 12 +- lib/web/mage/utils/compare.js | 2 +- lib/web/mage/utils/main.js | 2 +- lib/web/mage/utils/misc.js | 46 +- lib/web/mage/utils/objects.js | 2 +- lib/web/mage/utils/strings.js | 2 +- lib/web/mage/utils/template.js | 2 +- lib/web/mage/utils/wrapper.js | 2 +- lib/web/mage/validation.js | 948 ++- lib/web/mage/validation/url.js | 2 +- lib/web/mage/validation/validation.js | 100 +- lib/web/mage/view/composite.js | 26 +- lib/web/mage/webapi.js | 129 +- lib/web/mage/zoom.js | 158 +- lib/web/magnifier/magnifier.js | 2 +- lib/web/magnifier/magnify.js | 2 +- lib/web/moment-timezone-with-data.js | 7 + lib/web/prototype/window.js | 2 +- .../tiny_mce/classes/firebug/firebug-lite.js | 4 +- lib/web/tiny_mce/classes/util/Cookie.js | 2 +- lib/web/tiny_mce/tiny_mce.js | 2 +- lib/web/tiny_mce/tiny_mce_jquery.js | 2 +- lib/web/tiny_mce/tiny_mce_jquery_src.js | 2 +- lib/web/tiny_mce/tiny_mce_prototype.js | 2 +- lib/web/tiny_mce/tiny_mce_prototype_src.js | 2 +- lib/web/tiny_mce/tiny_mce_src.js | 2 +- lib/web/varien/form.js | 2 +- lib/web/varien/js.js | 2 +- nginx.conf.sample | 6 +- package.json.sample | 5 +- php.ini.sample | 2 +- phpserver/router.php | 8 +- pub/cron.php | 2 +- pub/errors/404.php | 2 +- pub/errors/503.php | 2 +- pub/errors/default/404.phtml | 2 +- pub/errors/default/503.phtml | 2 +- pub/errors/default/css/styles.css | 2 +- pub/errors/default/nocache.phtml | 2 +- pub/errors/default/page.phtml | 2 +- pub/errors/default/report.phtml | 2 +- pub/errors/design.xml | 2 +- pub/errors/local.xml.sample | 2 +- pub/errors/noCache.php | 2 +- pub/errors/processor.php | 2 +- pub/errors/processorFactory.php | 2 +- pub/errors/report.php | 2 +- pub/get.php | 2 +- pub/health_check.php | 76 + pub/index.php | 2 +- pub/media/.htaccess | 4 + pub/static.php | 2 +- pub/static/.htaccess | 9 +- setup/config/application.config.php | 2 +- setup/config/autoload/global.php | 2 +- setup/config/di.config.php | 3 +- setup/config/languages.config.php | 2 +- setup/config/marketplace.config.php | 2 +- setup/config/module.config.php | 2 +- setup/config/router.config.php | 2 +- setup/config/states.disable.config.php | 2 +- setup/config/states.enable.config.php | 2 +- .../config/states.extensionManager.config.php | 2 +- setup/config/states.home.config.php | 2 +- setup/config/states.install.config.php | 2 +- setup/config/states.uninstall.config.php | 2 +- setup/config/states.update.config.php | 2 +- setup/config/states.upgrade.config.php | 2 +- setup/index.php | 2 +- setup/performance-toolkit/benchmark.jmx | 326 +- .../config/attributeSets.xml | 132 + .../config/customerConfig.xml | 10 + .../config/description.xml | 27 + setup/performance-toolkit/config/di.xml | 9 + .../config/searchConfig.xml | 13 + .../config/searchTerms.xml | 17 + .../config/searchTermsLarge.xml | 669 ++ .../config/shortDescription.xml | 23 + .../profiles/ce/extra_large.xml | 7 +- .../performance-toolkit/profiles/ce/large.xml | 7 +- .../profiles/ce/medium.xml | 7 +- .../performance-toolkit/profiles/ce/small.xml | 45 +- .../pub/angular-clickout/angular-clickout.js | 25 + .../angular-clickout.min.js.map | 1 + .../pub/angular-sanitize/angular-sanitize.js | 618 ++ .../angular-sanitize.min.js.map | 8 + setup/pub/magento/setup/add-database.js | 2 +- setup/pub/magento/setup/app.js | 2 +- setup/pub/magento/setup/auth-dialog.js | 2 +- setup/pub/magento/setup/complete-backup.js | 2 +- .../pub/magento/setup/create-admin-account.js | 2 +- setup/pub/magento/setup/create-backup.js | 2 +- .../pub/magento/setup/customize-your-store.js | 2 +- setup/pub/magento/setup/data-option.js | 4 +- setup/pub/magento/setup/extension-grid.js | 2 +- setup/pub/magento/setup/home.js | 2 +- .../magento/setup/install-extension-grid.js | 2 +- setup/pub/magento/setup/install.js | 2 +- setup/pub/magento/setup/landing.js | 2 +- setup/pub/magento/setup/main.js | 2 +- .../magento/setup/marketplace-credentials.js | 2 +- setup/pub/magento/setup/module-grid.js | 2 +- setup/pub/magento/setup/readiness-check.js | 2 +- setup/pub/magento/setup/remove-dialog.js | 2 +- setup/pub/magento/setup/select-version.js | 2 +- setup/pub/magento/setup/start-updater.js | 2 +- setup/pub/magento/setup/success.js | 2 +- setup/pub/magento/setup/system-config.js | 2 +- .../magento/setup/update-extension-grid.js | 2 +- setup/pub/magento/setup/updater-success.js | 2 +- setup/pub/magento/setup/web-configuration.js | 28 +- setup/pub/styles/setup.css | 4 +- .../Command/AbstractDependenciesCommand.php | 2 +- .../Command/AbstractMaintenanceCommand.php | 2 +- .../Console/Command/AbstractModuleCommand.php | 2 +- .../Command/AbstractModuleManageCommand.php | 2 +- .../Console/Command/AbstractSetupCommand.php | 2 +- .../Command/AdminUserCreateCommand.php | 2 +- .../Setup/Console/Command/BackupCommand.php | 2 +- .../Console/Command/ConfigSetCommand.php | 2 +- .../Setup/Console/Command/CronRunCommand.php | 2 +- .../Console/Command/DbDataUpgradeCommand.php | 2 +- .../Command/DbSchemaUpgradeCommand.php | 2 +- .../Setup/Console/Command/DbStatusCommand.php | 2 +- .../DependenciesShowFrameworkCommand.php | 2 +- ...DependenciesShowModulesCircularCommand.php | 2 +- .../DependenciesShowModulesCommand.php | 2 +- .../Command/DeployStaticContentCommand.php | 52 +- .../Console/Command/DiCompileCommand.php | 6 +- .../Command/DiCompileMultiTenantCommand.php | 493 -- .../Command/GenerateFixturesCommand.php | 39 +- .../Command/I18nCollectPhrasesCommand.php | 2 +- .../Setup/Console/Command/I18nPackCommand.php | 2 +- .../Console/Command/InfoAdminUriCommand.php | 2 +- .../Command/InfoBackupsListCommand.php | 2 +- .../Command/InfoCurrencyListCommand.php | 2 +- .../Command/InfoLanguageListCommand.php | 2 +- .../Command/InfoTimezoneListCommand.php | 2 +- .../Setup/Console/Command/InstallCommand.php | 2 +- .../InstallStoreConfigurationCommand.php | 164 +- .../Command/MaintenanceAllowIpsCommand.php | 2 +- .../Command/MaintenanceDisableCommand.php | 2 +- .../Command/MaintenanceEnableCommand.php | 2 +- .../Command/MaintenanceStatusCommand.php | 2 +- .../Console/Command/ModuleDisableCommand.php | 2 +- .../Console/Command/ModuleEnableCommand.php | 2 +- .../Console/Command/ModuleStatusCommand.php | 2 +- .../Command/ModuleUninstallCommand.php | 2 +- .../Setup/Console/Command/RollbackCommand.php | 2 +- .../Console/Command/UninstallCommand.php | 2 +- .../Setup/Console/Command/UpgradeCommand.php | 10 +- .../src/Magento/Setup/Console/CommandList.php | 3 +- .../Setup/Console/CompilerPreparation.php | 70 +- .../Magento/Setup/Controller/AddDatabase.php | 2 +- .../Setup/Controller/BackupActionItems.php | 2 +- .../Setup/Controller/CompleteBackup.php | 2 +- .../Setup/Controller/CreateAdminAccount.php | 2 +- .../Magento/Setup/Controller/CreateBackup.php | 2 +- .../Setup/Controller/CustomizeYourStore.php | 2 +- .../Magento/Setup/Controller/DataOption.php | 2 +- .../Setup/Controller/DatabaseCheck.php | 2 +- .../Setup/Controller/DependencyCheck.php | 2 +- .../Magento/Setup/Controller/Environment.php | 2 +- .../Setup/Controller/ExtensionGrid.php | 2 +- setup/src/Magento/Setup/Controller/Home.php | 2 +- setup/src/Magento/Setup/Controller/Index.php | 2 +- .../src/Magento/Setup/Controller/Install.php | 2 +- .../Setup/Controller/InstallExtensionGrid.php | 2 +- .../Setup/Controller/LandingInstaller.php | 2 +- .../Setup/Controller/LandingUpdater.php | 2 +- .../src/Magento/Setup/Controller/License.php | 2 +- .../Magento/Setup/Controller/Maintenance.php | 2 +- .../Magento/Setup/Controller/Marketplace.php | 2 +- .../Controller/MarketplaceCredentials.php | 2 +- .../Magento/Setup/Controller/ModuleGrid.php | 2 +- .../src/Magento/Setup/Controller/Modules.php | 2 +- .../Magento/Setup/Controller/Navigation.php | 2 +- .../Setup/Controller/OtherComponentsGrid.php | 2 +- .../Controller/ReadinessCheckInstaller.php | 2 +- .../Controller/ReadinessCheckUpdater.php | 2 +- .../Controller/ResponseTypeInterface.php | 2 +- .../Setup/Controller/SelectVersion.php | 2 +- .../src/Magento/Setup/Controller/Session.php | 2 +- .../Magento/Setup/Controller/StartUpdater.php | 2 +- .../src/Magento/Setup/Controller/Success.php | 2 +- .../Magento/Setup/Controller/SystemConfig.php | 2 +- .../Setup/Controller/UpdateExtensionGrid.php | 2 +- .../Setup/Controller/UpdaterSuccess.php | 2 +- .../src/Magento/Setup/Controller/UrlCheck.php | 58 + .../Controller/ValidateAdminCredentials.php | 2 +- .../Setup/Controller/WebConfiguration.php | 2 +- setup/src/Magento/Setup/Exception.php | 2 +- .../Setup/Fixtures/AdminUsersFixture.php | 108 + .../AttributeSet/AttributeSetFixture.php | 171 + .../Setup/Fixtures/AttributeSet/Pattern.php | 95 + .../AttributeSet/SwatchesGenerator.php | 162 + .../Setup/Fixtures/AttributeSetsFixture.php | 99 + .../Setup/Fixtures/BundleProductsFixture.php | 261 + .../Setup/Fixtures/CartPriceRulesFixture.php | 4 +- .../Fixtures/CatalogPriceRulesFixture.php | 4 +- .../Setup/Fixtures/CategoriesFixture.php | 258 +- .../Setup/Fixtures/CategoryResolver.php | 104 + .../Setup/Fixtures/ConfigsApplyFixture.php | 2 +- .../Fixtures/ConfigurableProductsFixture.php | 1198 ++- .../Setup/Fixtures/CustomerGroupsFixture.php | 92 + .../Setup/Fixtures/CustomersFixture.php | 184 +- .../Setup/Fixtures/EavVariationsFixture.php | 127 +- setup/src/Magento/Setup/Fixtures/Fixture.php | 23 +- .../Magento/Setup/Fixtures/FixtureConfig.php | 75 + .../Magento/Setup/Fixtures/FixtureModel.php | 86 +- .../Fixtures/IndexersStatesApplyFixture.php | 2 +- .../Magento/Setup/Fixtures/OrdersFixture.php | 871 +- .../Magento/Setup/Fixtures/PriceProvider.php | 34 + .../Setup/Fixtures/ProductsAmountProvider.php | 46 + .../Setup/Fixtures/SimpleProductsFixture.php | 449 +- .../Magento/Setup/Fixtures/StoresFixture.php | 440 +- .../Setup/Fixtures/TaxRatesFixture.php | 10 +- .../Setup/Fixtures/TaxRulesFixture.php | 226 + .../Fixtures/WebsiteCategoryProvider.php | 155 + .../Setup/Fixtures/_files/dictionary.csv | 7373 +++++++++++++++++ .../Fixtures/_files/orders_fixture_data.json | 568 ++ .../Setup/Fixtures/{ => _files}/tax_rates.csv | 0 .../Setup/Fixtures/_files/tax_rates_small.csv | 133 + .../Model/Address/AddressDataGenerator.php | 24 + .../src/Magento/Setup/Model/AdminAccount.php | 2 +- .../Setup/Model/AdminAccountFactory.php | 2 +- .../Magento/Setup/Model/BasePackageInfo.php | 2 +- setup/src/Magento/Setup/Model/Bootstrap.php | 2 +- .../Magento/Setup/Model/Complex/Generator.php | 2 +- .../Magento/Setup/Model/Complex/Pattern.php | 4 +- .../Magento/Setup/Model/ConfigGenerator.php | 15 +- setup/src/Magento/Setup/Model/ConfigModel.php | 2 +- .../Magento/Setup/Model/ConfigOptionsList.php | 9 +- .../Model/ConfigOptionsListCollector.php | 2 +- .../Magento/Setup/Model/Cron/AbstractJob.php | 2 +- .../Model/Cron/Helper/ModuleUninstall.php | 2 +- .../Model/Cron/Helper/ThemeUninstall.php | 2 +- .../Model/Cron/JobComponentUninstall.php | 2 +- .../Setup/Model/Cron/JobDbRollback.php | 2 +- .../Magento/Setup/Model/Cron/JobFactory.php | 2 +- .../Magento/Setup/Model/Cron/JobModule.php | 2 +- .../Magento/Setup/Model/Cron/JobSetCache.php | 2 +- .../Model/Cron/JobSetMaintenanceMode.php | 2 +- .../Setup/Model/Cron/JobStaticRegenerate.php | 2 +- .../Magento/Setup/Model/Cron/JobUpgrade.php | 2 +- .../Setup/Model/Cron/MultipleStreamOutput.php | 2 +- setup/src/Magento/Setup/Model/Cron/Queue.php | 2 +- .../Magento/Setup/Model/Cron/Queue/Reader.php | 2 +- .../Magento/Setup/Model/Cron/Queue/Writer.php | 2 +- .../Setup/Model/Cron/ReadinessCheck.php | 2 +- .../Setup/Model/Cron/SetupLoggerFactory.php | 2 +- .../Setup/Model/Cron/SetupStreamHandler.php | 2 +- setup/src/Magento/Setup/Model/Cron/Status.php | 2 +- .../Setup/Model/CronScriptReadinessCheck.php | 2 +- .../Model/Customer/CustomerDataGenerator.php | 99 + .../Customer/CustomerDataGeneratorFactory.php | 44 + .../src/Magento/Setup/Model/DataGenerator.php | 87 + .../Setup/Model/DateTime/DateTimeProvider.php | 2 +- .../Setup/Model/DateTime/TimeZoneProvider.php | 2 +- .../Model/DefaultDescriptionGenerator.php | 34 + .../Setup/Model/DependencyReadinessCheck.php | 2 +- .../Description/DescriptionGenerator.php | 82 + .../DescriptionParagraphGenerator.php | 58 + .../DescriptionSentenceGenerator.php | 56 + .../Model/Description/Mixin/BoldMixin.php | 55 + .../Model/Description/Mixin/BrakeMixin.php | 26 + .../Mixin/DescriptionMixinInterface.php | 20 + .../Model/Description/Mixin/HeaderMixin.php | 36 + .../Mixin/Helper/RandomWordSelector.php | 36 + .../Description/Mixin/Helper/WordWrapper.php | 27 + .../Model/Description/Mixin/ItalicMixin.php | 55 + .../Model/Description/Mixin/MixinFactory.php | 76 + .../Description/Mixin/ParagraphMixin.php | 28 + .../Model/Description/Mixin/SpanMixin.php | 55 + .../Setup/Model/Description/MixinManager.php | 41 + .../Model/DescriptionGeneratorInterface.php | 20 + setup/src/Magento/Setup/Model/Dictionary.php | 77 + .../BundleProductGenerator.php | 119 + .../BundleProductTemplateGenerator.php | 164 + .../ConfigurableProductGenerator.php | 119 + .../ConfigurableProductTemplateGenerator.php | 164 + .../FixtureGenerator/CustomerGenerator.php | 158 + .../CustomerTemplateGenerator.php | 132 + .../FixtureGenerator/EntityGenerator.php | 471 ++ .../FixtureGenerator/ProductGenerator.php | 335 + .../ProductTemplateGeneratorFactory.php | 58 + .../SimpleProductTemplateGenerator.php | 97 + .../Model/FixtureGenerator/SqlCollector.php | 141 + .../TemplateEntityGeneratorInterface.php | 18 + setup/src/Magento/Setup/Model/Generator.php | 2 +- .../Magento/Setup/Model/Grid/Extension.php | 2 +- setup/src/Magento/Setup/Model/Grid/Module.php | 2 +- .../Magento/Setup/Model/Grid/TypeMapper.php | 2 +- setup/src/Magento/Setup/Model/Installer.php | 4 +- .../Setup/Model/Installer/Progress.php | 2 +- .../Setup/Model/Installer/ProgressFactory.php | 2 +- .../Magento/Setup/Model/InstallerFactory.php | 2 +- setup/src/Magento/Setup/Model/License.php | 2 +- .../src/Magento/Setup/Model/ModuleContext.php | 2 +- .../Setup/Model/ModuleRegistryUninstaller.php | 2 +- .../src/Magento/Setup/Model/ModuleStatus.php | 2 +- .../Setup/Model/ModuleStatusFactory.php | 2 +- .../Magento/Setup/Model/ModuleUninstaller.php | 2 +- setup/src/Magento/Setup/Model/Navigation.php | 2 +- .../Setup/Model/ObjectManagerProvider.php | 2 +- .../src/Magento/Setup/Model/PackagesAuth.php | 2 +- .../src/Magento/Setup/Model/PackagesData.php | 55 +- .../Magento/Setup/Model/PayloadValidator.php | 2 +- .../Magento/Setup/Model/PhpInformation.php | 6 +- .../Magento/Setup/Model/PhpReadinessCheck.php | 2 +- .../Setup/Model/RequestDataConverter.php | 2 +- .../Model/SearchTermDescriptionGenerator.php | 71 + .../SearchTermDescriptionGeneratorFactory.php | 147 + .../Magento/Setup/Model/SearchTermManager.php | 83 + .../Model/StoreConfigurationDataMapper.php | 2 +- .../src/Magento/Setup/Model/SystemPackage.php | 2 +- .../Model/ThemeDependencyCheckerFactory.php | 2 +- .../Setup/Model/UninstallCollector.php | 2 +- .../Setup/Model/UninstallDependencyCheck.php | 2 +- setup/src/Magento/Setup/Model/Updater.php | 2 +- .../Setup/Model/UpdaterTaskCreator.php | 2 +- setup/src/Magento/Setup/Model/WebLogger.php | 2 +- setup/src/Magento/Setup/Module.php | 2 +- .../Setup/Module/ConnectionFactory.php | 2 +- setup/src/Magento/Setup/Module/DataSetup.php | 2 +- .../Magento/Setup/Module/DataSetupFactory.php | 2 +- .../Setup/Module/Dependency/Circular.php | 2 +- .../Setup/Module/Dependency/Parser/Code.php | 2 +- .../Dependency/Parser/Composer/Json.php | 2 +- .../Module/Dependency/Parser/Config/Xml.php | 2 +- .../Module/Dependency/ParserInterface.php | 2 +- .../Report/Builder/AbstractBuilder.php | 2 +- .../Dependency/Report/BuilderInterface.php | 2 +- .../Dependency/Report/Circular/Builder.php | 2 +- .../Dependency/Report/Circular/Data/Chain.php | 2 +- .../Report/Circular/Data/Config.php | 2 +- .../Report/Circular/Data/Module.php | 2 +- .../Dependency/Report/Circular/Writer.php | 2 +- .../Report/Data/Config/AbstractConfig.php | 2 +- .../Report/Data/ConfigInterface.php | 2 +- .../Dependency/Report/Dependency/Builder.php | 2 +- .../Report/Dependency/Data/Config.php | 2 +- .../Report/Dependency/Data/Dependency.php | 2 +- .../Report/Dependency/Data/Module.php | 2 +- .../Dependency/Report/Dependency/Writer.php | 2 +- .../Dependency/Report/Framework/Builder.php | 2 +- .../Report/Framework/Data/Config.php | 2 +- .../Report/Framework/Data/Dependency.php | 2 +- .../Report/Framework/Data/Module.php | 2 +- .../Dependency/Report/Framework/Writer.php | 2 +- .../Report/Writer/Csv/AbstractWriter.php | 2 +- .../Dependency/Report/WriterInterface.php | 2 +- .../Module/Dependency/ServiceLocator.php | 2 +- .../Setup/Module/Di/App/Task/Manager.php | 2 +- .../Operation/ApplicationCodeGenerator.php | 2 +- .../Module/Di/App/Task/Operation/Area.php | 2 +- .../Di/App/Task/Operation/Interception.php | 2 +- .../App/Task/Operation/InterceptionCache.php | 2 +- .../Di/App/Task/Operation/ProxyGenerator.php | 2 +- .../Task/Operation/RepositoryGenerator.php | 2 +- .../ServiceDataAttributesGenerator.php | 2 +- .../Module/Di/App/Task/OperationException.php | 2 +- .../Module/Di/App/Task/OperationFactory.php | 2 +- .../Module/Di/App/Task/OperationInterface.php | 2 +- .../Setup/Module/Di/Code/Generator.php | 2 +- .../InterceptionConfigurationBuilder.php | 2 +- .../Module/Di/Code/Generator/Interceptor.php | 2 +- .../Module/Di/Code/Generator/PluginList.php | 7 +- .../Setup/Module/Di/Code/GeneratorFactory.php | 2 +- .../Di/Code/Reader/ClassReaderDecorator.php | 2 +- .../Module/Di/Code/Reader/ClassesScanner.php | 2 +- .../Code/Reader/ClassesScannerInterface.php | 2 +- .../Module/Di/Code/Reader/Decorator/Area.php | 2 +- .../Di/Code/Reader/Decorator/Directory.php | 2 +- .../Code/Reader/Decorator/Interceptions.php | 7 +- .../Module/Di/Code/Reader/FileScanner.php | 4 +- .../Setup/Module/Di/Code/Reader/Type.php | 2 +- .../Module/Di/Code/Scanner/ArrayScanner.php | 2 +- .../Di/Code/Scanner/CompositeScanner.php | 2 +- .../Di/Code/Scanner/ConfigurationScanner.php | 2 +- .../Di/Code/Scanner/DirectoryScanner.php | 2 +- .../Scanner/InheritanceInterceptorScanner.php | 2 +- .../Scanner/InterceptedInstancesScanner.php | 2 +- .../Module/Di/Code/Scanner/PhpScanner.php | 2 +- .../Module/Di/Code/Scanner/PluginScanner.php | 2 +- .../Di/Code/Scanner/RepositoryScanner.php | 2 +- .../Di/Code/Scanner/ScannerInterface.php | 2 +- .../Scanner/ServiceDataAttributesScanner.php | 2 +- .../Di/Code/Scanner/XmlInterceptorScanner.php | 2 +- .../Module/Di/Code/Scanner/XmlScanner.php | 2 +- .../Module/Di/Compiler/ArgumentsResolver.php | 2 +- .../Di/Compiler/ArgumentsResolverFactory.php | 2 +- .../Config/Chain/ArgumentsSerialization.php | 2 +- .../Compiler/Config/Chain/BackslashTrim.php | 2 +- .../Config/Chain/InterceptorSubstitution.php | 2 +- .../Config/Chain/PreferencesResolving.php | 2 +- .../Di/Compiler/Config/ModificationChain.php | 2 +- .../Compiler/Config/ModificationInterface.php | 2 +- .../Module/Di/Compiler/Config/Reader.php | 2 +- .../Di/Compiler/Config/Writer/Filesystem.php | 35 +- .../Di/Compiler/Config/WriterInterface.php | 2 +- .../Di/Compiler/ConstructorArgument.php | 2 +- .../Setup/Module/Di/Compiler/Log/Log.php | 2 +- .../Module/Di/Compiler/Log/Writer/Console.php | 2 +- .../Setup/Module/Di/Definition/Collection.php | 2 +- .../src/Magento/Setup/Module/I18n/Context.php | 2 +- .../Magento/Setup/Module/I18n/Dictionary.php | 2 +- .../Module/I18n/Dictionary/Generator.php | 2 +- .../Dictionary/Loader/File/AbstractFile.php | 2 +- .../I18n/Dictionary/Loader/File/Csv.php | 2 +- .../I18n/Dictionary/Loader/FileInterface.php | 2 +- .../I18n/Dictionary/Options/Resolver.php | 2 +- .../Dictionary/Options/ResolverFactory.php | 2 +- .../Dictionary/Options/ResolverInterface.php | 2 +- .../Setup/Module/I18n/Dictionary/Phrase.php | 2 +- .../Module/I18n/Dictionary/Writer/Csv.php | 18 +- .../I18n/Dictionary/Writer/Csv/Stdo.php | 2 +- .../I18n/Dictionary/WriterInterface.php | 2 +- .../src/Magento/Setup/Module/I18n/Factory.php | 2 +- .../Setup/Module/I18n/FilesCollector.php | 2 +- .../src/Magento/Setup/Module/I18n/Locale.php | 2 +- .../Setup/Module/I18n/Pack/Generator.php | 2 +- .../I18n/Pack/Writer/File/AbstractFile.php | 2 +- .../Module/I18n/Pack/Writer/File/Csv.php | 2 +- .../Module/I18n/Pack/WriterInterface.php | 2 +- .../Module/I18n/Parser/AbstractParser.php | 2 +- .../I18n/Parser/Adapter/AbstractAdapter.php | 2 +- .../Setup/Module/I18n/Parser/Adapter/Html.php | 2 +- .../Setup/Module/I18n/Parser/Adapter/Js.php | 2 +- .../Setup/Module/I18n/Parser/Adapter/Php.php | 2 +- .../I18n/Parser/Adapter/Php/Tokenizer.php | 2 +- .../Adapter/Php/Tokenizer/PhraseCollector.php | 2 +- .../Parser/Adapter/Php/Tokenizer/Token.php | 2 +- .../Tokenizer/Translate/MethodCollector.php | 2 +- .../Setup/Module/I18n/Parser/Adapter/Xml.php | 2 +- .../Module/I18n/Parser/AdapterInterface.php | 2 +- .../Setup/Module/I18n/Parser/Contextual.php | 2 +- .../Setup/Module/I18n/Parser/Parser.php | 2 +- .../Setup/Module/I18n/ParserInterface.php | 2 +- .../Setup/Module/I18n/ServiceLocator.php | 2 +- .../Magento/Setup/Module/ResourceFactory.php | 2 +- setup/src/Magento/Setup/Module/Setup.php | 2 +- .../Setup/Module/Setup/ResourceConfig.php | 2 +- .../Magento/Setup/Module/Setup/SetupCache.php | 2 +- .../src/Magento/Setup/Module/SetupFactory.php | 2 +- .../Setup/Mvc/Bootstrap/InitParamListener.php | 18 +- .../Mvc/View/Http/InjectTemplateListener.php | 2 +- .../Command/AdminUserCreateCommandTest.php | 2 +- .../Console/Command/BackupCommandTest.php | 2 +- .../Console/Command/ConfigSetCommandTest.php | 2 +- .../Console/Command/CronRunCommandTest.php | 2 +- .../Command/DbDataUpgradeCommandTest.php | 2 +- .../Command/DbSchemaUpgradeCommandTest.php | 2 +- .../Console/Command/DbStatusCommandTest.php | 2 +- .../DeployStaticContentCommandTest.php | 6 +- .../Console/Command/DiCompileCommandTest.php | 2 +- .../Console/Command/FunctionExistMock.php | 2 +- .../Command/GenerateFixturesCommandTest.php | 6 +- .../Command/InfoAdminUriCommandTest.php | 2 +- .../Command/InfoBackupsListCommandTest.php | 2 +- .../Command/InfoCurrencyListCommandTest.php | 2 +- .../Command/InfoLanguageListCommandTest.php | 2 +- .../Command/InfoTimezoneListCommandTest.php | 2 +- .../Console/Command/InstallCommandTest.php | 2 +- .../InstallStoreConfigurationCommandTest.php | 107 +- .../MaintenanceAllowIpsCommandTest.php | 2 +- .../Command/MaintenanceDisableCommandTest.php | 2 +- .../Command/MaintenanceEnableCommandTest.php | 2 +- .../Command/MaintenanceStatusCommandTest.php | 2 +- .../ModuleEnableDisableCommandTest.php | 2 +- .../Command/ModuleStatusCommandTest.php | 2 +- .../Command/ModuleUninstallCommandTest.php | 2 +- .../Console/Command/RollbackCommandTest.php | 2 +- .../Console/Command/UninstallCommandTest.php | 2 +- .../Console/Command/UpgradeCommandTest.php | 30 +- .../Test/Unit/Console/CommandListTest.php | 2 +- .../Unit/Console/CompilerPreparationTest.php | 113 +- .../Test/Unit/Controller/AddDatabaseTest.php | 2 +- .../Unit/Controller/BackupActionItemsTest.php | 2 +- .../Unit/Controller/CompleteBackupTest.php | 2 +- .../Controller/CreateAdminAccountTest.php | 2 +- .../Test/Unit/Controller/CreateBackupTest.php | 2 +- .../Controller/CustomizeYourStoreTest.php | 2 +- .../Test/Unit/Controller/DataOptionTest.php | 2 +- .../Test/Unit/Controller/EnvironmentTest.php | 2 +- .../Unit/Controller/ExtensionGridTest.php | 2 +- .../Setup/Test/Unit/Controller/IndexTest.php | 2 +- .../Controller/InstallExtensionGridTest.php | 2 +- .../Test/Unit/Controller/InstallTest.php | 2 +- .../Unit/Controller/LandingInstallerTest.php | 2 +- .../Unit/Controller/LandingUpdaterTest.php | 2 +- .../Test/Unit/Controller/LicenseTest.php | 2 +- .../Test/Unit/Controller/MaintenanceTest.php | 2 +- .../Test/Unit/Controller/MarketplaceTest.php | 2 +- .../Test/Unit/Controller/ModuleGridTest.php | 2 +- .../Test/Unit/Controller/ModulesTest.php | 2 +- .../Test/Unit/Controller/NavigationTest.php | 2 +- .../Controller/OtherComponentsGridTest.php | 2 +- .../ReadinessCheckInstallerTest.php | 2 +- .../Controller/ReadinessCheckUpdaterTest.php | 2 +- .../Unit/Controller/SelectVersionTest.php | 2 +- .../Test/Unit/Controller/SessionTest.php | 2 +- .../Test/Unit/Controller/StartUpdaterTest.php | 2 +- .../Test/Unit/Controller/SuccessTest.php | 2 +- .../Test/Unit/Controller/SystemConfigTest.php | 2 +- .../Controller/UpdateExtensionGridTest.php | 2 +- .../Unit/Controller/UpdaterSuccessTest.php | 2 +- .../Test/Unit/Controller/UrlCheckTest.php | 143 + .../Unit/Controller/WebConfigurationTest.php | 2 +- .../AttributeSet/AttributeSetFixtureTest.php | 161 + .../Fixtures/AttributeSet/PatternTest.php | 62 + .../AttributeSet/SwatchesGeneratorTest.php | 73 + .../Fixtures/AttributeSetsFixtureTest.php | 112 + .../Fixtures/CartPriceRulesFixtureTest.php | 2 +- .../Fixtures/CatalogPriceRulesFixtureTest.php | 2 +- .../Unit/Fixtures/CategoriesFixtureTest.php | 140 +- .../Unit/Fixtures/ConfigsApplyFixtureTest.php | 2 +- .../ConfigurableProductsFixtureTest.php | 272 +- .../Fixtures/CustomerGroupsFixtureTest.php | 123 + .../Unit/Fixtures/CustomersFixtureTest.php | 175 +- .../Fixtures/EavVariationsFixtureTest.php | 221 +- .../Test/Unit/Fixtures/FixtureConfigTest.php | 80 + .../Test/Unit/Fixtures/FixtureModelTest.php | 60 +- .../IndexersStatesApplyFixtureTest.php | 2 +- .../Test/Unit/Fixtures/OrdersFixtureTest.php | 298 +- .../Fixtures/SimpleProductsFixtureTest.php | 150 - .../Test/Unit/Fixtures/StoresFixtureTest.php | 317 +- .../Unit/Fixtures/TaxRatesFixtureTest.php | 2 +- .../Unit/Fixtures/TaxRulesFixtureTest.php | 131 + .../Address/AddressDataGeneratorTest.php | 46 + .../Unit/Model/AdminAccountFactoryTest.php | 2 +- .../Test/Unit/Model/AdminAccountTest.php | 2 +- .../Test/Unit/Model/BasePackageInfoTest.php | 2 +- .../Model/Complex/ComplexGeneratorTest.php | 2 +- .../Test/Unit/Model/Complex/PatternTest.php | 10 +- .../Test/Unit/Model/ConfigGeneratorTest.php | 2 +- .../Setup/Test/Unit/Model/ConfigModelTest.php | 2 +- .../Test/Unit/Model/ConfigOptionsListTest.php | 32 +- .../Model/Cron/Helper/ModuleUninstallTest.php | 2 +- .../Model/Cron/Helper/ThemeUninstallTest.php | 2 +- .../Model/Cron/JobComponentUninstallTest.php | 2 +- .../Unit/Model/Cron/JobDbRollbackTest.php | 2 +- .../Test/Unit/Model/Cron/JobFactoryTest.php | 2 +- .../Test/Unit/Model/Cron/JobModuleTest.php | 2 +- .../Test/Unit/Model/Cron/JobSetCacheTest.php | 14 +- .../Model/Cron/JobSetMaintenanceModeTest.php | 2 +- .../Model/Cron/JobStaticRegenerateTest.php | 2 +- .../Test/Unit/Model/Cron/JobUpgradeTest.php | 2 +- .../Test/Unit/Model/Cron/Queue/ReaderTest.php | 2 +- .../Test/Unit/Model/Cron/Queue/WriterTest.php | 2 +- .../Setup/Test/Unit/Model/Cron/QueueTest.php | 2 +- .../Unit/Model/Cron/ReadinessCheckTest.php | 2 +- .../Setup/Test/Unit/Model/Cron/StatusTest.php | 2 +- .../Model/CronScriptReadinessCheckTest.php | 2 +- .../Customer/CustomerDataGeneratorTest.php | 102 + .../Test/Unit/Model/DataGeneratorTest.php | 50 + .../Model/DateTime/DateTimeProviderTest.php | 2 +- .../Model/DateTime/TimeZoneProviderTest.php | 2 +- .../Model/DependencyReadinessCheckTest.php | 2 +- .../Description/DescriptionGeneratorTest.php | 108 + .../DescriptionParagraphGeneratorTest.php | 74 + .../DescriptionSentenceGeneratorTest.php | 59 + .../Model/Description/Mixin/BoldMixinTest.php | 73 + .../Description/Mixin/BrakeMixinTest.php | 45 + .../Description/Mixin/HeaderMixinTest.php | 46 + .../Mixin/Helper/RandomWordSelectorTest.php | 60 + .../Mixin/Helper/WordWrapperTest.php | 64 + .../Description/Mixin/ItalicMixinTest.php | 73 + .../Description/Mixin/ParagraphMixinTest.php | 43 + .../Model/Description/Mixin/SpanMixinTest.php | 73 + .../Model/Description/MixinManagerTest.php | 82 + .../Setup/Test/Unit/Model/DictionaryTest.php | 59 + .../FixtureGenerator/SqlCollectorTest.php | 123 + .../Setup/Test/Unit/Model/GeneratorTest.php | 2 +- .../Test/Unit/Model/Grid/ExtensionTest.php | 2 +- .../Setup/Test/Unit/Model/Grid/ModuleTest.php | 2 +- .../Test/Unit/Model/Grid/TypeMapperTest.php | 2 +- .../Model/Installer/ProgressFactoryTest.php | 2 +- .../Unit/Model/Installer/ProgressTest.php | 2 +- .../Test/Unit/Model/InstallerFactoryTest.php | 2 +- .../Setup/Test/Unit/Model/InstallerTest.php | 2 +- .../Setup/Test/Unit/Model/LicenseTest.php | 2 +- .../Test/Unit/Model/ModuleContextTest.php | 2 +- .../Model/ModuleRegistryUninstallerTest.php | 2 +- .../Unit/Model/ModuleStatusFactoryTest.php | 2 +- .../Test/Unit/Model/ModuleStatusTest.php | 2 +- .../Test/Unit/Model/ModuleUninstallerTest.php | 2 +- .../Setup/Test/Unit/Model/NavigationTest.php | 2 +- .../Unit/Model/ObjectManagerProviderTest.php | 2 +- .../Test/Unit/Model/PackagesAuthTest.php | 2 +- .../Test/Unit/Model/PackagesDataTest.php | 193 +- .../Test/Unit/Model/PayloadValidatorTest.php | 2 +- .../Test/Unit/Model/PhpInformationTest.php | 2 +- .../Test/Unit/Model/PhpReadinessCheckTest.php | 2 +- .../SearchTermDescriptionGeneratorTest.php | 70 + .../Test/Unit/Model/SearchTermManagerTest.php | 76 + .../StoreConfigurationDataMapperTest.php | 2 +- .../Test/Unit/Model/SystemPackageTest.php | 2 +- .../ThemeDependencyCheckerFactoryTest.php | 2 +- .../Unit/Model/UninstallCollectorTest.php | 2 +- .../Model/UninstallDependencyCheckTest.php | 2 +- .../Unit/Model/UpdaterTaskCreatorTest.php | 2 +- .../Setup/Test/Unit/Model/UpdaterTest.php | 2 +- .../Setup/Test/Unit/Model/WebLoggerTest.php | 2 +- .../Test/Unit/Model/_files/dictionary.csv | 5 + .../Test/Unit/Module/ConfigGeneratorTest.php | 10 +- .../Unit/Module/ConnectionFactoryTest.php | 2 +- .../Test/Unit/Module/DataSetupFactoryTest.php | 2 +- .../Module/Dependency/Parser/CodeTest.php | 2 +- .../Dependency/Parser/Composer/JsonTest.php | 2 +- .../Dependency/Parser/Config/XmlTest.php | 2 +- .../Report/Builder/AbstractBuilderTest.php | 2 +- .../Report/Circular/Data/ChainTest.php | 2 +- .../Report/Circular/Data/ConfigTest.php | 2 +- .../Report/Circular/Data/ModuleTest.php | 2 +- .../Report/Data/Config/AbstractConfigTest.php | 2 +- .../Report/Dependency/Data/ConfigTest.php | 2 +- .../Report/Dependency/Data/DependencyTest.php | 2 +- .../Report/Dependency/Data/ModuleTest.php | 2 +- .../Report/Framework/BuilderTest.php | 2 +- .../Report/Framework/Data/ConfigTest.php | 2 +- .../Report/Framework/Data/DependencyTest.php | 2 +- .../Report/Framework/Data/ModuleTest.php | 2 +- .../Report/Writer/Csv/AbstractWriterTest.php | 2 +- .../App/Task/ApplicationCodeGeneratorTest.php | 2 +- .../Test/Unit/Module/Di/App/Task/AreaTest.php | 2 +- .../Di/App/Task/InterceptionCacheTest.php | 2 +- .../Di/App/Task/OperationFactoryTest.php | 2 +- .../Module/Di/App/Task/ProxyGeneratorTest.php | 2 +- .../Di/App/Task/RepositoryGeneratorTest.php | 2 +- .../ServiceDataAttributesGeneratorTest.php | 2 +- .../InterceptionConfigurationBuilderTest.php | 2 +- .../Code/Reader/ClassReaderDecoratorTest.php | 2 +- .../Di/Code/Reader/ClassesScannerTest.php | 2 +- .../Reader/InstancesNamesList/AreaTest.php | 2 +- .../InstancesNamesList/DirectoryTest.php | 14 +- .../InstancesNamesList/InterceptionsTest.php | 7 +- .../Di/Code/Scanner/ArrayScannerTest.php | 2 +- .../Di/Code/Scanner/CompositeScannerTest.php | 2 +- .../Code/Scanner/ConfigurationScannerTest.php | 2 +- .../Di/Code/Scanner/DirectoryScannerTest.php | 2 +- .../Module/Di/Code/Scanner/PhpScannerTest.php | 2 +- .../Di/Code/Scanner/PluginScannerTest.php | 2 +- .../ServiceDataAttributesScannerTest.php | 2 +- .../Scanner/XmlInterceptorScannerTest.php | 2 +- .../Module/Di/Code/Scanner/XmlScannerTest.php | 2 +- .../Di/Compiler/ArgumentsResolverTest.php | 2 +- .../Chain/ArgumentsSerializationTest.php | 2 +- .../Config/Chain/BackslashTrimTest.php | 2 +- .../Chain/InterceptorSubstitutionTest.php | 2 +- .../Config/Chain/PreferencesResolvingTest.php | 2 +- .../Compiler/Config/ModificationChainTest.php | 2 +- .../Module/Di/Compiler/Config/ReaderTest.php | 2 +- .../Di/Compiler/ConstructorArgumentTest.php | 2 +- .../Module/Di/Definition/CollectionTest.php | 2 +- .../Test/Unit/Module/Di/_files/additional.php | 2 +- .../Unit/Module/Di/_files/app/bootstrap.php | 2 +- .../app/code/Magento/SomeModule/Element.php | 2 +- .../Magento/SomeModule/ElementFactory.php | 2 +- .../code/Magento/SomeModule/Helper/Test.php | 2 +- .../Magento/SomeModule/Model/DoubleColon.php | 2 +- .../code/Magento/SomeModule/Model/Test.php | 2 +- .../SomeModule/etc/adminhtml/system.xml | 2 +- .../app/code/Magento/SomeModule/etc/di.xml | 2 +- .../SomeModule/etc/source/PhpExt.php/content | 2 +- .../SomeModule/view/frontend/default.xml | 2 +- .../adminhtml/default/backend/layout.xml | 2 +- .../Module/Di/_files/app/etc/additional.xml | 2 +- .../Unit/Module/Di/_files/app/etc/config.xml | 2 +- .../Module/Di/_files/app/etc/di/config.xml | 2 +- .../Module/Di/_files/extension_attributes.xml | 2 +- .../Test/Unit/Module/I18n/ContextTest.php | 2 +- .../Module/I18n/Dictionary/GeneratorTest.php | 2 +- .../Loader/File/AbstractFileTest.php | 2 +- .../Options/ResolverFactoryTest.php | 2 +- .../I18n/Dictionary/Options/ResolverTest.php | 2 +- .../Module/I18n/Dictionary/PhraseTest.php | 2 +- .../I18n/Dictionary/Writer/Csv/StdoTest.php | 23 +- .../Module/I18n/Dictionary/Writer/CsvTest.php | 2 +- .../Test/Unit/Module/I18n/DictionaryTest.php | 2 +- .../Test/Unit/Module/I18n/FactoryTest.php | 2 +- .../Unit/Module/I18n/FilesCollectorTest.php | 2 +- .../Test/Unit/Module/I18n/LocaleTest.php | 2 +- .../Unit/Module/I18n/Pack/GeneratorTest.php | 2 +- .../Module/I18n/Pack/Writer/File/CsvTest.php | 2 +- .../I18n/Pack/Writer/File/_files/ioMock.php | 2 +- .../Module/I18n/Parser/AbstractParserTest.php | 2 +- .../Parser/Adapter/AbstractAdapterTest.php | 2 +- .../Module/I18n/Parser/Adapter/HtmlTest.php | 2 +- .../Module/I18n/Parser/Adapter/JsTest.php | 2 +- .../Php/Tokenizer/PhraseCollectorTest.php | 2 +- .../Adapter/Php/Tokenizer/TokenTest.php | 2 +- .../I18n/Parser/Adapter/Php/TokenizerTest.php | 2 +- .../Module/I18n/Parser/Adapter/PhpTest.php | 2 +- .../Module/I18n/Parser/Adapter/XmlTest.php | 2 +- .../I18n/Parser/Adapter/_files/default.xml | 2 +- .../I18n/Parser/Adapter/_files/email.html | 2 +- .../Module/I18n/Parser/Adapter/_files/file.js | 2 +- .../Unit/Module/I18n/Parser/ParserTest.php | 2 +- .../I18n/_files/files_collector/default.xml | 2 +- .../I18n/_files/files_collector/file.js | 2 +- .../Test/Unit/Module/ResourceFactoryTest.php | 2 +- .../Unit/Module/Setup/ResourceConfigTest.php | 2 +- .../Test/Unit/Module/Setup/SetupCacheTest.php | 2 +- .../Test/Unit/Module/SetupFactoryTest.php | 2 +- .../Setup/Test/Unit/Module/SetupTest.php | 2 +- .../Mvc/Bootstrap/InitParamListenerTest.php | 207 +- .../Test/Unit/Validator/DbValidatorTest.php | 2 +- .../Test/Unit/Validator/IpValidatorTest.php | 2 +- .../Validator/AdminCredentialsValidator.php | 2 +- .../Magento/Setup/Validator/DbValidator.php | 2 +- .../Magento/Setup/Validator/IpValidator.php | 2 +- setup/view/error/401.phtml | 2 +- setup/view/error/404.phtml | 2 +- setup/view/error/index.phtml | 2 +- setup/view/layout/layout.phtml | 2 +- setup/view/magento/setup/add-database.phtml | 2 +- .../setup/complete-backup/progress.phtml | 2 +- .../magento/setup/create-admin-account.phtml | 2 +- setup/view/magento/setup/create-backup.phtml | 4 +- .../magento/setup/customize-your-store.phtml | 2 +- setup/view/magento/setup/data-option.phtml | 2 +- setup/view/magento/setup/extension-grid.phtml | 2 +- setup/view/magento/setup/home.phtml | 2 +- setup/view/magento/setup/index.phtml | 2 +- .../setup/install-extension-grid.phtml | 2 +- setup/view/magento/setup/install.phtml | 2 +- setup/view/magento/setup/landing.phtml | 2 +- setup/view/magento/setup/license.phtml | 2 +- .../setup/marketplace-credentials.phtml | 2 +- setup/view/magento/setup/module-grid.phtml | 2 +- .../magento/setup/navigation/header-bar.phtml | 4 +- .../view/magento/setup/navigation/menu.phtml | 2 +- .../magento/setup/navigation/side-menu.phtml | 2 +- setup/view/magento/setup/popupauth.phtml | 2 +- .../view/magento/setup/readiness-check.phtml | 2 +- .../setup/readiness-check/progress.phtml | 2 +- setup/view/magento/setup/select-version.phtml | 2 +- setup/view/magento/setup/start-updater.phtml | 2 +- setup/view/magento/setup/success.phtml | 2 +- setup/view/magento/setup/system-config.phtml | 2 +- .../magento/setup/update-extension-grid.phtml | 2 +- .../view/magento/setup/updater-success.phtml | 2 +- .../magento/setup/web-configuration.phtml | 11 +- setup/view/styles/lib/variables/_buttons.less | 2 +- setup/view/styles/lib/variables/_colors.less | 2 +- .../styles/lib/variables/_typography.less | 2 +- 24822 files changed, 236298 insertions(+), 68528 deletions(-) create mode 100644 app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPriceType.php create mode 100644 app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/TierPriceTypeTest.php create mode 100644 app/code/Magento/Analytics/Api/Data/LinkInterface.php create mode 100644 app/code/Magento/Analytics/Api/LinkProviderInterface.php create mode 100644 app/code/Magento/Analytics/Block/Adminhtml/System/Config/SubscriptionStatusLabel.php create mode 100644 app/code/Magento/Analytics/Controller/Adminhtml/BasicTier/SignUp.php create mode 100644 app/code/Magento/Analytics/Controller/Adminhtml/Reports/Show.php create mode 100644 app/code/Magento/Analytics/Controller/Adminhtml/Subscription/Activate.php create mode 100644 app/code/Magento/Analytics/Controller/Adminhtml/Subscription/Postpone.php create mode 100644 app/code/Magento/Analytics/Cron/CollectData.php create mode 100644 app/code/Magento/Analytics/Cron/SignUp.php create mode 100644 app/code/Magento/Analytics/Cron/Update.php create mode 100644 app/code/Magento/Analytics/LICENSE.txt create mode 100644 app/code/Magento/Analytics/LICENSE_AFL.txt create mode 100644 app/code/Magento/Analytics/Model/AnalyticsToken.php create mode 100644 app/code/Magento/Analytics/Model/Condition/CanViewNotification.php create mode 100644 app/code/Magento/Analytics/Model/Config.php create mode 100644 app/code/Magento/Analytics/Model/Config/Backend/CollectionTime.php create mode 100644 app/code/Magento/Analytics/Model/Config/Backend/Enabled.php create mode 100644 app/code/Magento/Analytics/Model/Config/Backend/Enabled/SubscriptionHandler.php create mode 100644 app/code/Magento/Analytics/Model/Config/Backend/Vertical.php create mode 100644 app/code/Magento/Analytics/Model/Config/Data.php create mode 100644 app/code/Magento/Analytics/Model/Config/Mapper.php create mode 100644 app/code/Magento/Analytics/Model/Config/Reader.php create mode 100644 app/code/Magento/Analytics/Model/Config/Reader/Xml.php create mode 100644 app/code/Magento/Analytics/Model/Config/SchemaLocator.php create mode 100644 app/code/Magento/Analytics/Model/Config/Source/Vertical.php create mode 100644 app/code/Magento/Analytics/Model/ConfigInterface.php create mode 100644 app/code/Magento/Analytics/Model/Connector.php create mode 100644 app/code/Magento/Analytics/Model/Connector/CommandInterface.php create mode 100644 app/code/Magento/Analytics/Model/Connector/Http/Client/Curl.php create mode 100644 app/code/Magento/Analytics/Model/Connector/Http/ClientInterface.php create mode 100644 app/code/Magento/Analytics/Model/Connector/Http/ResponseFactory.php create mode 100644 app/code/Magento/Analytics/Model/Connector/OTPRequest.php create mode 100644 app/code/Magento/Analytics/Model/Connector/SignUpCommand.php create mode 100644 app/code/Magento/Analytics/Model/Connector/SignUpRequest.php create mode 100644 app/code/Magento/Analytics/Model/Connector/UpdateCommand.php create mode 100644 app/code/Magento/Analytics/Model/Cryptographer.php create mode 100644 app/code/Magento/Analytics/Model/EncodedContext.php create mode 100644 app/code/Magento/Analytics/Model/ExportDataHandler.php create mode 100644 app/code/Magento/Analytics/Model/FileInfo.php create mode 100644 app/code/Magento/Analytics/Model/FileInfoManager.php create mode 100644 app/code/Magento/Analytics/Model/FileRecorder.php create mode 100644 app/code/Magento/Analytics/Model/FlagManager.php create mode 100644 app/code/Magento/Analytics/Model/IntegrationManager.php create mode 100644 app/code/Magento/Analytics/Model/Link.php create mode 100644 app/code/Magento/Analytics/Model/LinkProvider.php create mode 100644 app/code/Magento/Analytics/Model/NotificationTime.php create mode 100644 app/code/Magento/Analytics/Model/Plugin/BaseUrlConfigPlugin.php create mode 100644 app/code/Magento/Analytics/Model/ProviderFactory.php create mode 100644 app/code/Magento/Analytics/Model/ReportUrlProvider.php create mode 100644 app/code/Magento/Analytics/Model/ReportWriter.php create mode 100644 app/code/Magento/Analytics/Model/ReportWriterInterface.php create mode 100644 app/code/Magento/Analytics/Model/ReportXml/ModuleIterator.php create mode 100644 app/code/Magento/Analytics/Model/StoreConfigurationProvider.php create mode 100644 app/code/Magento/Analytics/Model/Subscription.php create mode 100644 app/code/Magento/Analytics/Model/SubscriptionStatusProvider.php create mode 100644 app/code/Magento/Analytics/README.md create mode 100644 app/code/Magento/Analytics/ReportXml/Config.php create mode 100644 app/code/Magento/Analytics/ReportXml/Config/Converter/Xml.php create mode 100644 app/code/Magento/Analytics/ReportXml/Config/Data.php create mode 100644 app/code/Magento/Analytics/ReportXml/Config/Mapper.php create mode 100644 app/code/Magento/Analytics/ReportXml/Config/Reader.php create mode 100644 app/code/Magento/Analytics/ReportXml/Config/Reader/Xml.php create mode 100644 app/code/Magento/Analytics/ReportXml/Config/SchemaLocator.php create mode 100644 app/code/Magento/Analytics/ReportXml/ConfigInterface.php create mode 100644 app/code/Magento/Analytics/ReportXml/ConnectionFactory.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/Assembler/AssemblerInterface.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/Assembler/FilterAssembler.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/Assembler/FromAssembler.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/Assembler/JoinAssembler.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/ColumnsResolver.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/ConditionResolver.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/NameResolver.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/ReportValidator.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/SelectBuilder.php create mode 100644 app/code/Magento/Analytics/ReportXml/DB/SelectBuilderFactory.php create mode 100644 app/code/Magento/Analytics/ReportXml/IteratorFactory.php create mode 100644 app/code/Magento/Analytics/ReportXml/Query.php create mode 100644 app/code/Magento/Analytics/ReportXml/QueryFactory.php create mode 100644 app/code/Magento/Analytics/ReportXml/ReportProvider.php create mode 100644 app/code/Magento/Analytics/ReportXml/SelectHydrator.php create mode 100644 app/code/Magento/Analytics/Setup/InstallData.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/BasicTier/SignUpTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Reports/ShowTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Subscription/ActivateTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Subscription/PostponeTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Cron/CollectDataTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Cron/SignUpTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/AnalyticsTokenTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Condition/CanViewNotificationTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/CollectionTimeTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/Enabled/SubscriptionHandlerTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/EnabledTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/VerticalTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Config/MapperTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Config/ReaderTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Config/SchemaLocatorTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Config/Source/VerticalTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/ConfigTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/Client/CurlTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Connector/OTPRequestTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpRequestTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Connector/UpdateCommandTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/ConnectorTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/CryptographerTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/EncodedContextTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/ExportDataHandlerTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/FileInfoManagerTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/FileInfoTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/FileRecorderTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/FlagManagerTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/IntegrationManagerTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/LinkProviderTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/NotificationTimeTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/Plugin/BaseUrlConfigPluginTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/ReportUrlProviderTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/ReportWriterTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/ReportXml/ModuleIteratorTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/StoreConfigurationProviderTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/SubscriptionStatusProviderTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Model/SubscriptionTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/Config/Converter/XmlTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/Config/MapperTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/Config/SchemaLocatorTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/Config/_files/valid_reports.xml create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/ConfigTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/ConnectionFactoryTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/FilterAssemblerTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/FromAssemblerTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/JoinAssemblerTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ColumnsResolverTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ConditionResolverTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/DB/NameResolverTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ReportValidatorTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/DB/SelectBuilderTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/IteratorFactoryTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/QueryFactoryTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/QueryTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/ReportProviderTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/ReportXml/SelectHydratorTest.php create mode 100644 app/code/Magento/Analytics/Test/Unit/Ui/DataProvider/DummyDataProviderTest.php create mode 100644 app/code/Magento/Analytics/Ui/DataProvider/DummyDataProvider.php create mode 100644 app/code/Magento/Analytics/composer.json create mode 100644 app/code/Magento/Analytics/etc/acl.xml create mode 100644 app/code/Magento/Analytics/etc/adminhtml/menu.xml create mode 100644 app/code/Magento/Analytics/etc/adminhtml/routes.xml create mode 100644 app/code/Magento/Analytics/etc/adminhtml/system.xml create mode 100644 app/code/Magento/Analytics/etc/analytics.xml create mode 100644 app/code/Magento/Analytics/etc/analytics.xsd create mode 100644 app/code/Magento/Analytics/etc/config.xml create mode 100644 app/code/Magento/Analytics/etc/crontab.xml create mode 100644 app/code/Magento/Analytics/etc/di.xml create mode 100644 app/code/Magento/Analytics/etc/module.xml create mode 100644 app/code/Magento/Analytics/etc/reports.xml create mode 100644 app/code/Magento/Analytics/etc/reports.xsd create mode 100644 app/code/Magento/Analytics/etc/webapi.xml create mode 100644 app/code/Magento/Analytics/registration.php create mode 100644 app/code/Magento/Analytics/view/adminhtml/layout/adminhtml_dashboard_index.xml create mode 100644 app/code/Magento/Analytics/view/adminhtml/templates/dashboard/link.phtml create mode 100644 app/code/Magento/Analytics/view/adminhtml/ui_component/analytics_subscription_form.xml create mode 100644 app/code/Magento/Analytics/view/adminhtml/web/js/modal/modal-component.js create mode 100644 app/code/Magento/Authorization/Test/Unit/Model/ResourceModel/RulesTest.php create mode 100644 app/code/Magento/Backend/Block/AnchorRenderer.php create mode 100644 app/code/Magento/Backend/Block/Cache/Grid/Massaction/ProductionModeVisibilityChecker.php create mode 100644 app/code/Magento/Backend/Block/MenuItemChecker.php create mode 100644 app/code/Magento/Backend/Block/Widget/Grid/Massaction/VisibilityCheckerInterface.php create mode 100644 app/code/Magento/Backend/Model/View/Layout/ConditionInterface.php create mode 100644 app/code/Magento/Backend/Model/View/Layout/ConditionPool.php create mode 100644 app/code/Magento/Backend/Model/View/Layout/Filter.php create mode 100644 app/code/Magento/Backend/Model/View/Layout/Filter/Condition.php create mode 100644 app/code/Magento/Backend/Model/View/Layout/FilterInterface.php create mode 100644 app/code/Magento/Backend/Model/View/Layout/StructureManager.php create mode 100644 app/code/Magento/Backend/Test/Unit/Block/AnchorRendererTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Block/MenuItemCheckerTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Block/MenuTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Store/IndexTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Model/View/Layout/ConditionPoolTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Model/View/Layout/Filter/ConditionTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Model/View/Layout/FilterTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Model/View/Layout/StructureManagerTest.php create mode 100644 app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_constructor_data.php create mode 100644 app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php create mode 100644 app/code/Magento/Braintree/Model/Ui/Adminhtml/PayPal/TokenUiComponentProvider.php create mode 100644 app/code/Magento/Braintree/Setup/UpgradeData.php create mode 100644 app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/PayPal/TokenUiComponentProviderTest.php create mode 100644 app/code/Magento/Braintree/view/adminhtml/templates/form/paypal/vault.phtml create mode 100644 app/code/Magento/Bundle/Pricing/Adjustment/DefaultSelectionPriceListProvider.php create mode 100644 app/code/Magento/Bundle/Pricing/Adjustment/SelectionPriceListProviderInterface.php mode change 100644 => 100755 app/code/Magento/Bundle/Setup/UpgradeSchema.php create mode 100644 app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js create mode 100644 app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows.js create mode 100644 app/code/Magento/Catalog/Api/BasePriceStorageInterface.php create mode 100644 app/code/Magento/Catalog/Api/CostStorageInterface.php create mode 100644 app/code/Magento/Catalog/Api/Data/BasePriceInterface.php create mode 100644 app/code/Magento/Catalog/Api/Data/CostInterface.php create mode 100644 app/code/Magento/Catalog/Api/Data/PriceUpdateResultInterface.php create mode 100644 app/code/Magento/Catalog/Api/Data/SpecialPriceInterface.php create mode 100644 app/code/Magento/Catalog/Api/Data/TierPriceInterface.php create mode 100644 app/code/Magento/Catalog/Api/SpecialPriceInterface.php create mode 100644 app/code/Magento/Catalog/Api/SpecialPriceStorageInterface.php create mode 100644 app/code/Magento/Catalog/Api/TierPriceStorageInterface.php create mode 100644 app/code/Magento/Catalog/Cron/DeleteOutdatedPriceValues.php create mode 100644 app/code/Magento/Catalog/Model/Category/FileInfo.php create mode 100644 app/code/Magento/Catalog/Model/Config/Source/Product/Options/TierPrice.php create mode 100644 app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/Builder.php create mode 100644 app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/BuilderInterface.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/BasePrice.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/Cost.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/CostStorage.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/PriceUpdateResult.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/SpecialPrice.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/TierPrice.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/Validation/InvalidSkuProcessor.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/Validation/Result.php create mode 100644 app/code/Magento/Catalog/Model/Product/Price/Validation/TierPriceValidator.php create mode 100644 app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php create mode 100644 app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolverInterface.php create mode 100644 app/code/Magento/Catalog/Model/ProductIdLocator.php create mode 100644 app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/BaseSelectProcessorInterface.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/CompositeBaseSelectProcessor.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/Price/SpecialPrice.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/StatusBaseSelectProcessor.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/Website/SelectProcessor.php create mode 100644 app/code/Magento/Catalog/Model/View/Asset/Image.php create mode 100644 app/code/Magento/Catalog/Model/View/Asset/Image/Context.php create mode 100644 app/code/Magento/Catalog/Model/View/Asset/Placeholder.php create mode 100644 app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php create mode 100644 app/code/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChange.php create mode 100644 app/code/Magento/Catalog/Pricing/Price/MinimalPriceCalculatorInterface.php create mode 100644 app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php mode change 100644 => 100755 app/code/Magento/Catalog/Setup/UpgradeSchema.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/Image/UploadTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/AddAttributeToTemplateTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Controller/Category/MoveTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Helper/Product/ConfigurationTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/FlatTableBuilderTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Table/BuilderTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Price/BasePriceStorageTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/InvalidSkuProcessorTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Pricing/Renderer/SalableResolverTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CompositeBaseSelectProcessorTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/StatusBaseSelectProcessorTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/View/Asset/Image/ContextTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/View/Asset/PlaceholderTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Pricing/Price/MinimalTierPriceCalculatorTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/TierPriceTest.php create mode 100644 app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php create mode 100644 app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js delete mode 100644 app/code/Magento/Catalog/view/adminhtml/web/js/form/element/price-input.js create mode 100644 app/code/Magento/Catalog/view/adminhtml/web/js/tier-price/percentage-processor.js create mode 100644 app/code/Magento/Catalog/view/adminhtml/web/js/tier-price/value-type-select.js create mode 100644 app/code/Magento/Catalog/view/adminhtml/web/js/utils/percentage-price-calculator.js delete mode 100644 app/code/Magento/Catalog/view/adminhtml/web/template/form/element/price-input.html delete mode 100644 app/code/Magento/Catalog/view/frontend/web/js/compare.js create mode 100644 app/code/Magento/CatalogInventory/Model/Indexer/Stock/CacheCleaner.php create mode 100644 app/code/Magento/CatalogInventory/Model/ResourceModel/Product/StockStatusBaseSelectProcessor.php create mode 100644 app/code/Magento/CatalogInventory/Setup/UpgradeSchema.php create mode 100644 app/code/Magento/CatalogInventory/Test/Unit/Model/Indexer/Stock/CacheCleanerTest.php create mode 100644 app/code/Magento/CatalogInventory/Test/Unit/Model/ResourceModel/Product/StockStatusBaseSelectProcessorTest.php create mode 100644 app/code/Magento/CatalogInventoryConfigurableProduct/LICENSE.txt create mode 100644 app/code/Magento/CatalogInventoryConfigurableProduct/LICENSE_AFL.txt create mode 100644 app/code/Magento/CatalogInventoryConfigurableProduct/Plugin/GetInStockAttributeOptionsPlugin.php create mode 100644 app/code/Magento/CatalogInventoryConfigurableProduct/README.md create mode 100644 app/code/Magento/CatalogInventoryConfigurableProduct/Test/Unit/Plugin/GetInStockAttributeOptionsPluginTest.php create mode 100644 app/code/Magento/CatalogInventoryConfigurableProduct/composer.json create mode 100644 app/code/Magento/CatalogInventoryConfigurableProduct/etc/frontend/di.xml create mode 100644 app/code/Magento/CatalogInventoryConfigurableProduct/etc/module.xml create mode 100644 app/code/Magento/CatalogInventoryConfigurableProduct/registration.php create mode 100644 app/code/Magento/CatalogRule/Model/ResourceModel/Product/CollectionProcessor.php create mode 100644 app/code/Magento/CatalogRule/Setup/UpgradeData.php create mode 100644 app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/AliasResolver.php create mode 100644 app/code/Magento/CatalogSearch/Model/Attribute/SearchWeight.php create mode 100644 app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherInterface.php create mode 100644 app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherProxy.php create mode 100644 app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexSwitcher.php create mode 100644 app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexTableNotExistException.php create mode 100644 app/code/Magento/CatalogSearch/Model/Indexer/Scope/ScopeProxy.php create mode 100644 app/code/Magento/CatalogSearch/Model/Indexer/Scope/State.php create mode 100644 app/code/Magento/CatalogSearch/Model/Indexer/Scope/TemporaryResolver.php create mode 100644 app/code/Magento/CatalogSearch/Model/Indexer/Scope/UnknownStateException.php create mode 100644 app/code/Magento/CatalogSearch/Model/Search/FilterMapper/ExclusionStrategy.php create mode 100644 app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterContext.php create mode 100644 app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterStrategyInterface.php create mode 100644 app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StaticAttributeStrategy.php create mode 100644 app/code/Magento/CatalogSearch/Model/Search/FilterMapper/TermDropdownStrategy.php create mode 100644 app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/Decimal.php create mode 100644 app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/General.php create mode 100644 app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/GeneratorInterface.php create mode 100644 app/code/Magento/CatalogSearch/Model/Search/RequestGenerator/GeneratorResolver.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/AliasResolverTest.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Attribute/SearchWeightTest.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/IndexerHandlerFactoryTest.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Scope/IndexSwitcherTest.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Search/FilterMapper/FilterContextTest.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/DecimalTest.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/GeneralTest.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGenerator/GeneratorResolverTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/CategoryBasedProductRewriteGenerator.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/DataCategoryHashMap.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/DataCategoryUrlRewriteDatabaseMap.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/DataCategoryUsedInProductsHashMap.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/DataProductHashMap.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/DataProductUrlRewriteDatabaseMap.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/DatabaseMapInterface.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/DatabaseMapPool.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/HashMapInterface.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/HashMapPool.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Map/UrlRewriteFinder.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/UrlRewriteBunchReplacer.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/CategoryBasedProductRewriteGeneratorTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Map/DataCategoryHashMapTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Map/DataCategoryUrlRewriteDatabaseMapTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Map/DataCategoryUsedInProductsHashMapTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Map/DataProductHashMapTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Map/DataProductUrlRewriteDatabaseMapTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Map/HashMapPoolTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Map/UrlRewriteFinderTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/UrlRewriteBunchReplacerTest.php create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/UrlRewriteHandlerTest.php create mode 100644 app/code/Magento/Checkout/Block/Cart/Grid.php create mode 100644 app/code/Magento/Checkout/Block/Checkout/DirectoryDataProcessor.php create mode 100644 app/code/Magento/Checkout/Model/Adminhtml/BillingAddressDisplayOptions.php create mode 100644 app/code/Magento/Checkout/Model/Cart/CheckoutSummaryConfigProvider.php create mode 100644 app/code/Magento/Checkout/Model/Cart/RequestInfoFilter.php create mode 100644 app/code/Magento/Checkout/Model/Cart/RequestInfoFilterComposite.php create mode 100644 app/code/Magento/Checkout/Model/Cart/RequestInfoFilterInterface.php create mode 100644 app/code/Magento/Checkout/Test/Unit/Block/Cart/GridTest.php create mode 100644 app/code/Magento/Checkout/Test/Unit/Block/Checkout/DirectoryDataProcessorTest.php create mode 100644 app/code/Magento/Checkout/Test/Unit/Block/Checkout/LayoutProcessorTest.php create mode 100644 app/code/Magento/Checkout/Test/Unit/Model/Cart/CheckoutSummaryConfigProviderTest.php create mode 100644 app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterCompositeTest.php create mode 100644 app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterTest.php create mode 100644 app/code/Magento/Checkout/view/frontend/web/js/model/cart/cache.js create mode 100644 app/code/Magento/Checkout/view/frontend/web/js/model/payment/method-group.js create mode 100644 app/code/Magento/Checkout/view/frontend/web/js/view/configure/product-customer-data.js create mode 100644 app/code/Magento/Checkout/view/frontend/web/template/shipping-address/shipping-method-item.html create mode 100644 app/code/Magento/Checkout/view/frontend/web/template/shipping-address/shipping-method-list.html create mode 100644 app/code/Magento/Cms/Test/Unit/Controller/RouterTest.php create mode 100644 app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php create mode 100644 app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php create mode 100644 app/code/Magento/CmsUrlRewrite/Test/Unit/Model/CmsPageUrlRewriteGeneratorTest.php create mode 100644 app/code/Magento/Config/App/Config/Source/DumpConfigSourceAggregated.php create mode 100644 app/code/Magento/Config/App/Config/Source/DumpConfigSourceInterface.php create mode 100644 app/code/Magento/Config/App/Config/Source/EnvironmentConfigSource.php create mode 100644 app/code/Magento/Config/App/Config/Source/ModularConfigSource.php create mode 100644 app/code/Magento/Config/App/Config/Source/RuntimeConfigSource.php create mode 100644 app/code/Magento/Config/App/Config/Type/System.php create mode 100644 app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorFactory.php create mode 100644 app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorInterface.php create mode 100644 app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php create mode 100644 app/code/Magento/Config/Console/Command/ConfigSet/LockProcessor.php create mode 100644 app/code/Magento/Config/Console/Command/ConfigSetCommand.php create mode 100644 app/code/Magento/Config/Console/Command/ConfigShow/ValueProcessor.php create mode 100644 app/code/Magento/Config/Console/Command/ConfigShowCommand.php create mode 100644 app/code/Magento/Config/Console/CommandList.php create mode 100644 app/code/Magento/Config/Model/Config/Export/Comment.php create mode 100644 app/code/Magento/Config/Model/Config/Export/ExcludeList.php create mode 100644 app/code/Magento/Config/Model/Config/Parser/Comment.php create mode 100644 app/code/Magento/Config/Model/Config/Processor/EnvironmentPlaceholder.php create mode 100644 app/code/Magento/Config/Model/Config/Reader/Source/Deployed/SettingChecker.php create mode 100644 app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php create mode 100644 app/code/Magento/Config/Model/Config/Structure/ElementVisibilityComposite.php create mode 100644 app/code/Magento/Config/Model/Config/Structure/ElementVisibilityInterface.php create mode 100644 app/code/Magento/Config/Model/Placeholder/Environment.php create mode 100644 app/code/Magento/Config/Model/Placeholder/PlaceholderFactory.php create mode 100644 app/code/Magento/Config/Model/Placeholder/PlaceholderInterface.php create mode 100644 app/code/Magento/Config/Test/Unit/App/Config/Source/DumpConfigSourceAggregatedTest.php create mode 100644 app/code/Magento/Config/Test/Unit/App/Config/Source/EnvironmentConfigSourceTest.php create mode 100644 app/code/Magento/Config/Test/Unit/App/Config/Source/ModularConfigSourceTest.php create mode 100644 app/code/Magento/Config/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php create mode 100644 app/code/Magento/Config/Test/Unit/App/Config/Type/SystemTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ConfigSetProcessorFactoryTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockProcessorTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Console/Command/ConfigShow/ValueProcessorTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Console/Command/ConfigShowCommandTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Console/CommandListTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Export/CommentTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Export/ExcludeListTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Parser/CommentTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Processor/EnvironmentPlaceholderTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Reader/Source/Deployed/SettingCheckerTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibilityCompositeTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/_files/config.local.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Placeholder/EnvironmentTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Placeholder/PlaceholderFactoryTest.php create mode 100644 app/code/Magento/Config/cli_commands.php delete mode 100644 app/code/Magento/ConfigurableProduct/Block/Plugin/Product/Media/Gallery.php create mode 100644 app/code/Magento/ConfigurableProduct/Model/AttributeOptionProvider.php create mode 100644 app/code/Magento/ConfigurableProduct/Model/AttributeOptionProviderInterface.php create mode 100644 app/code/Magento/ConfigurableProduct/Model/Product/Cache/Tag/Configurable.php create mode 100644 app/code/Magento/ConfigurableProduct/Model/ResourceModel/Attribute/OptionProvider.php create mode 100644 app/code/Magento/ConfigurableProduct/Plugin/Model/Attribute/Backend/AttributeValidation.php delete mode 100644 app/code/Magento/ConfigurableProduct/Plugin/Model/Product.php create mode 100644 app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProvider.php create mode 100644 app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProviderInterface.php create mode 100644 app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Block/Plugin/Product/Media/GalleryTest.php create mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Model/AttributeOptionProviderTest.php create mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Cache/Tag/ConfigurableTest.php create mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Attribute/OptionProviderTest.php create mode 100644 app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml create mode 100644 app/code/Magento/Contact/Model/Config.php create mode 100644 app/code/Magento/Contact/Model/ConfigInterface.php create mode 100644 app/code/Magento/Contact/Model/Mail.php create mode 100644 app/code/Magento/Contact/Model/MailInterface.php create mode 100644 app/code/Magento/Contact/Test/Unit/Model/MailTest.php create mode 100644 app/code/Magento/Contact/etc/di.xml create mode 100644 app/code/Magento/Cron/Console/Command/CronInstallCommand.php create mode 100644 app/code/Magento/Cron/Console/Command/CronRemoveCommand.php create mode 100644 app/code/Magento/Cron/Test/Unit/Console/Command/CronInstallCommandTest.php create mode 100644 app/code/Magento/Cron/Test/Unit/Console/Command/CronRemoveCommandTest.php create mode 100644 app/code/Magento/CurrencySymbol/Setup/UpgradeData.php create mode 100644 app/code/Magento/Customer/Block/Account/Delimiter.php create mode 100644 app/code/Magento/Customer/Block/Account/Navigation.php create mode 100644 app/code/Magento/Customer/Block/Account/SortLink.php create mode 100644 app/code/Magento/Customer/Block/Account/SortLinkInterface.php create mode 100644 app/code/Magento/Customer/Block/Widget/Company.php create mode 100644 app/code/Magento/Customer/Block/Widget/Fax.php create mode 100644 app/code/Magento/Customer/Block/Widget/Telephone.php create mode 100644 app/code/Magento/Customer/Model/Config/Backend/Show/AddressOnly.php create mode 100644 app/code/Magento/Customer/Model/Indexer/Source.php create mode 100644 app/code/Magento/Customer/Model/Metadata/AttributeMetadataCache.php create mode 100644 app/code/Magento/Customer/Model/Metadata/AttributeMetadataHydrator.php create mode 100644 app/code/Magento/Customer/Model/ResourceModel/Customer/Indexer/Collection.php mode change 100644 => 100755 app/code/Magento/Customer/Setup/UpgradeSchema.php create mode 100644 app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataCacheTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataHydratorTest.php create mode 100644 app/code/Magento/Customer/view/frontend/templates/account/navigation-delimiter.phtml create mode 100644 app/code/Magento/Customer/view/frontend/templates/widget/company.phtml create mode 100644 app/code/Magento/Customer/view/frontend/templates/widget/fax.phtml create mode 100644 app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml create mode 100644 app/code/Magento/Customer/view/frontend/web/js/addressValidation.js create mode 100644 app/code/Magento/CustomerAnalytics/LICENSE.txt create mode 100644 app/code/Magento/CustomerAnalytics/LICENSE_AFL.txt create mode 100644 app/code/Magento/CustomerAnalytics/README.md create mode 100644 app/code/Magento/CustomerAnalytics/composer.json create mode 100644 app/code/Magento/CustomerAnalytics/etc/analytics.xml create mode 100644 app/code/Magento/CustomerAnalytics/etc/module.xml create mode 100644 app/code/Magento/CustomerAnalytics/etc/reports.xml create mode 100644 app/code/Magento/CustomerAnalytics/registration.php create mode 100644 app/code/Magento/Deploy/Console/Command/App/ApplicationDumpCommand.php create mode 100644 app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSet/CollectorFactory.php create mode 100644 app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSet/CollectorInterface.php create mode 100644 app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSet/InteractiveCollector.php create mode 100644 app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSet/SimpleCollector.php create mode 100644 app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommand.php create mode 100644 app/code/Magento/Deploy/Model/ConfigWriter.php create mode 100644 app/code/Magento/Deploy/Model/Deploy/JsDictionaryDeploy.php create mode 100644 app/code/Magento/Deploy/Test/Unit/Console/Command/App/SensitiveConfigSet/CollectorFactoryTest.php create mode 100644 app/code/Magento/Deploy/Test/Unit/Console/Command/App/SensitiveConfigSet/InteractiveCollectorTest.php create mode 100644 app/code/Magento/Deploy/Test/Unit/Console/Command/App/SensitiveConfigSet/SimpleCollectorTest.php create mode 100644 app/code/Magento/Deploy/Test/Unit/Console/Command/App/SensitiveConfigSetCommandTest.php create mode 100644 app/code/Magento/Deploy/Test/Unit/Console/Command/ApplicationDumpCommandTest.php create mode 100644 app/code/Magento/Deploy/Test/Unit/Model/ConfigWriterTest.php create mode 100644 app/code/Magento/Deploy/Test/Unit/Model/Deploy/JsDictionaryDeployTest.php create mode 100755 app/code/Magento/Downloadable/Setup/UpgradeSchema.php rename {lib/internal/Magento/Framework/EntityManager => app/code/Magento/Eav/Model}/CustomAttributesMapper.php (95%) create mode 100644 app/code/Magento/Eav/Model/Entity/Attribute/AttributeGroupAlreadyExistsException.php create mode 100644 app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php create mode 100644 app/code/Magento/Eav/Setup/UpgradeSchema.php rename {lib/internal/Magento/Framework/EntityManager/Test/Unit => app/code/Magento/Eav/Test/Unit/Model}/CustomAttributesMapperTest.php (87%) rename app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Backend/{ArrayTest.php => ArrayBackendTest.php} (86%) create mode 100644 app/code/Magento/Email/Model/Mail/TransportInterfacePlugin.php create mode 100644 app/code/Magento/Fedex/etc/di.xml create mode 100644 app/code/Magento/GiftMessage/Test/Unit/Model/OrderItemRepositoryTest.php create mode 100644 app/code/Magento/GoogleAnalytics/Test/Unit/Block/GaTest.php create mode 100644 app/code/Magento/Indexer/Setup/RecurringData.php create mode 100644 app/code/Magento/Msrp/Setup/UpgradeData.php create mode 100644 app/code/Magento/Multishipping/Test/Unit/Block/Checkout/Billing/ItemsTest.php create mode 100644 app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php rename app/code/Magento/PageCache/Block/System/Config/Form/Field/Export/{Varnish3.php => Varnish5.php} (68%) create mode 100644 app/code/Magento/PageCache/Model/Layout/MergePlugin.php create mode 100644 app/code/Magento/PageCache/etc/frontend/events.xml delete mode 100644 app/code/Magento/PageCache/etc/varnish3.vcl create mode 100644 app/code/Magento/PageCache/etc/varnish5.vcl create mode 100644 app/code/Magento/Payment/Plugin/PaymentConfigurationProcess.php create mode 100644 app/code/Magento/Payment/Test/Unit/Plugin/PaymentConfigurationProcessTest.php create mode 100644 app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-data.js create mode 100644 app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator.js create mode 100644 app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator/credit-card-type.js create mode 100644 app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator/luhn10-validator.js create mode 100644 app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/cvv-validator.js create mode 100644 app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/expiration-date-validator.js create mode 100644 app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/expiration-date-validator/expiration-month-validator.js create mode 100644 app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/expiration-date-validator/expiration-year-validator.js create mode 100644 app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/expiration-date-validator/parse-date.js rename app/code/Magento/Payment/view/{frontend => base}/web/js/model/credit-card-validation/validator.js (63%) delete mode 100644 app/code/Magento/Payment/view/frontend/web/js/model/credit-card-validation/credit-card-data.js delete mode 100644 app/code/Magento/Payment/view/frontend/web/js/model/credit-card-validation/credit-card-number-validator.js delete mode 100644 app/code/Magento/Payment/view/frontend/web/js/model/credit-card-validation/credit-card-number-validator/credit-card-type.js delete mode 100644 app/code/Magento/Payment/view/frontend/web/js/model/credit-card-validation/credit-card-number-validator/luhn10-validator.js delete mode 100644 app/code/Magento/Payment/view/frontend/web/js/model/credit-card-validation/cvv-validator.js delete mode 100644 app/code/Magento/Payment/view/frontend/web/js/model/credit-card-validation/expiration-date-validator.js delete mode 100644 app/code/Magento/Payment/view/frontend/web/js/model/credit-card-validation/expiration-date-validator/expiration-month-validator.js delete mode 100644 app/code/Magento/Payment/view/frontend/web/js/model/credit-card-validation/expiration-date-validator/expiration-year-validator.js delete mode 100644 app/code/Magento/Payment/view/frontend/web/js/model/credit-card-validation/expiration-date-validator/parse-date.js create mode 100644 app/code/Magento/Paypal/Model/Config/Structure/PaymentSectionModifier.php create mode 100644 app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/PaymentSectionModifierTest.php create mode 100644 app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/_files/payment_section_structure_variations.php create mode 100644 app/code/Magento/Quote/Model/ShippingAddressAssignment.php create mode 100644 app/code/Magento/Quote/Setup/ConvertSerializedDataToJson.php create mode 100644 app/code/Magento/Quote/Setup/UpgradeData.php create mode 100644 app/code/Magento/Quote/Test/Unit/Model/ResourceModel/QuoteTest.php create mode 100644 app/code/Magento/Quote/Test/Unit/Model/ShippingAddressAssignmentTest.php create mode 100644 app/code/Magento/Rule/Test/Unit/Model/AbstractModelTest.php rename app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/{Void.php => VoidAction.php} (96%) rename app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/{Void.php => VoidAction.php} (91%) create mode 100644 app/code/Magento/Sales/Model/Order/Validation/InvoiceOrder.php create mode 100644 app/code/Magento/Sales/Model/Order/Validation/InvoiceOrderInterface.php create mode 100644 app/code/Magento/Sales/Model/Order/Validation/RefundInvoice.php create mode 100644 app/code/Magento/Sales/Model/Order/Validation/RefundInvoiceInterface.php create mode 100644 app/code/Magento/Sales/Model/Order/Validation/RefundOrder.php create mode 100644 app/code/Magento/Sales/Model/Order/Validation/RefundOrderInterface.php create mode 100644 app/code/Magento/Sales/Model/Order/Validation/ShipOrder.php create mode 100644 app/code/Magento/Sales/Model/Order/Validation/ShipOrderInterface.php create mode 100644 app/code/Magento/Sales/Model/ValidatorResult.php create mode 100644 app/code/Magento/Sales/Model/ValidatorResultInterface.php create mode 100644 app/code/Magento/Sales/Model/ValidatorResultMerger.php create mode 100644 app/code/Magento/Sales/Setup/ConvertSerializedDataToJson.php create mode 100644 app/code/Magento/Sales/Setup/SerializedDataConverter.php create mode 100644 app/code/Magento/Sales/Test/Unit/Block/Order/PrintShipmentTest.php create mode 100644 app/code/Magento/Sales/Test/Unit/Block/Status/Grid/Column/StateTest.php rename app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/{VoidTest.php => VoidActionTest.php} (98%) rename app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/{VoidTest.php => VoidActionTest.php} (98%) create mode 100644 app/code/Magento/Sales/Test/Unit/CustomerData/LastOrderedItemsTest.php create mode 100644 app/code/Magento/Sales/Test/Unit/Model/Order/Address/RendererTest.php create mode 100644 app/code/Magento/Sales/Test/Unit/Setup/SerializedDataConverterTest.php create mode 100644 app/code/Magento/SalesAnalytics/LICENSE.txt create mode 100644 app/code/Magento/SalesAnalytics/LICENSE_AFL.txt create mode 100644 app/code/Magento/SalesAnalytics/README.md create mode 100644 app/code/Magento/SalesAnalytics/composer.json create mode 100644 app/code/Magento/SalesAnalytics/etc/analytics.xml create mode 100644 app/code/Magento/SalesAnalytics/etc/module.xml create mode 100644 app/code/Magento/SalesAnalytics/etc/reports.xml create mode 100644 app/code/Magento/SalesAnalytics/registration.php create mode 100644 app/code/Magento/SalesInventory/LICENSE.txt create mode 100644 app/code/Magento/SalesInventory/LICENSE_AFL.txt create mode 100644 app/code/Magento/SalesInventory/Model/Order/ReturnProcessor.php create mode 100644 app/code/Magento/SalesInventory/Model/Order/ReturnValidator.php create mode 100644 app/code/Magento/SalesInventory/Model/Plugin/Order/ReturnToStockInvoice.php create mode 100644 app/code/Magento/SalesInventory/Model/Plugin/Order/ReturnToStockOrder.php create mode 100644 app/code/Magento/SalesInventory/Model/Plugin/Order/Validation/InvoiceRefundCreationArguments.php create mode 100644 app/code/Magento/SalesInventory/Model/Plugin/Order/Validation/OrderRefundCreationArguments.php rename app/code/Magento/{CatalogInventory => SalesInventory}/Observer/RefundOrderInventoryObserver.php (55%) create mode 100644 app/code/Magento/SalesInventory/README.md create mode 100644 app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnProcessorTest.php create mode 100644 app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnValidatorTest.php create mode 100644 app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/ReturnToStockInvoiceTest.php create mode 100644 app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/ReturnToStockOrderTest.php create mode 100644 app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/Validation/InvoiceRefundCreationArgumentsTest.php create mode 100644 app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/Validation/OrderRefundCreationArgumentsTest.php rename app/code/Magento/{CatalogInventory => SalesInventory}/Test/Unit/Observer/RefundOrderInventoryObserverTest.php (64%) create mode 100644 app/code/Magento/SalesInventory/composer.json create mode 100644 app/code/Magento/SalesInventory/etc/di.xml create mode 100644 app/code/Magento/SalesInventory/etc/events.xml create mode 100644 app/code/Magento/SalesInventory/etc/extension_attributes.xml create mode 100644 app/code/Magento/SalesInventory/etc/module.xml create mode 100644 app/code/Magento/SalesInventory/registration.php create mode 100644 app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Widget/Chooser.php create mode 100644 app/code/Magento/SalesRule/Setup/UpgradeData.php create mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php create mode 100644 app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/ExportSearchCsvTest.php create mode 100644 app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/IndexTest.php create mode 100644 app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/ReportTest.php create mode 100644 app/code/Magento/Store/App/Config/Source/RuntimeConfigSource.php create mode 100644 app/code/Magento/Store/App/Config/Type/Scopes.php create mode 100644 app/code/Magento/Store/Console/Command/StoreListCommand.php create mode 100644 app/code/Magento/Store/Console/Command/WebsiteListCommand.php create mode 100644 app/code/Magento/Store/Model/Config/Placeholder.php create mode 100644 app/code/Magento/Store/Model/Config/Processor/Fallback.php delete mode 100644 app/code/Magento/Store/Model/Config/Reader/DefaultReader.php delete mode 100644 app/code/Magento/Store/Model/Config/Reader/ReaderPool.php create mode 100644 app/code/Magento/Store/Model/Config/Reader/Source/Dynamic/DefaultScope.php create mode 100644 app/code/Magento/Store/Model/Config/Reader/Source/Dynamic/Store.php create mode 100644 app/code/Magento/Store/Model/Config/Reader/Source/Dynamic/Website.php create mode 100644 app/code/Magento/Store/Model/Config/Reader/Source/Initial/DefaultScope.php create mode 100644 app/code/Magento/Store/Model/Config/Reader/Source/Initial/Store.php create mode 100644 app/code/Magento/Store/Model/Config/Reader/Source/Initial/Website.php delete mode 100644 app/code/Magento/Store/Model/Config/Reader/Store.php delete mode 100644 app/code/Magento/Store/Model/Config/Reader/Website.php create mode 100644 app/code/Magento/Store/Test/Unit/App/Config/Source/RuntimeConfigSourceTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Console/Command/StoreListCommandTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Console/Command/WebsiteListCommandTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/PlaceholderTest.php delete mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/DefaultReaderTest.php delete mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/ReaderPoolTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/Source/Dynamic/DefaultScopeTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/Source/Dynamic/StoreTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/Source/Dynamic/WebsiteTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/Source/Initial/DefaultScopeTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/Source/Initial/StoreTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/Source/Initial/WebsiteTest.php delete mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/StoreTest.php delete mode 100644 app/code/Magento/Store/Test/Unit/Model/Config/Reader/WebsiteTest.php create mode 100644 app/code/Magento/Store/Test/Unit/Model/StoreRepositoryTest.php create mode 100644 app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js create mode 100644 app/code/Magento/Tax/Model/System/Message/Notification/ApplyDiscountOnPrices.php create mode 100644 app/code/Magento/Tax/Model/System/Message/Notification/DiscountErrors.php create mode 100644 app/code/Magento/Tax/Model/System/Message/Notification/RoundingErrors.php create mode 100644 app/code/Magento/Tax/Model/System/Message/NotificationInterface.php create mode 100644 app/code/Magento/Tax/Test/Unit/Model/System/Message/Notification/ApplyDiscountOnPricesTest.php create mode 100644 app/code/Magento/Tax/Test/Unit/Model/System/Message/Notification/DiscountErrorsTest.php create mode 100644 app/code/Magento/Tax/Test/Unit/Model/System/Message/Notification/RoundingErrorsTest.php create mode 100644 app/code/Magento/Tax/Test/Unit/Model/System/Message/NotificationsTest.php create mode 100644 app/code/Magento/TaxImportExport/Test/Unit/Controller/Adminhtml/Rate/ExportPostTest.php create mode 100644 app/code/Magento/Theme/Model/Source/InitialThemeSource.php create mode 100644 app/code/Magento/Theme/Test/Unit/Model/Source/InitialThemeSourceTest.php create mode 100644 app/code/Magento/Translation/App/Config/Type/Translation.php create mode 100644 app/code/Magento/Translation/Model/Source/InitialTranslationSource.php create mode 100644 app/code/Magento/Translation/Test/Unit/App/Config/Type/TranslationTest.php create mode 100644 app/code/Magento/Translation/Test/Unit/Model/Source/InitialTranslationSourceTest.php create mode 100644 app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php create mode 100644 app/code/Magento/Ui/Test/Unit/Component/MassAction/FilterTest.php create mode 100644 app/code/Magento/Ui/view/adminhtml/web/templates/modal/modal-prompt-content.html create mode 100644 app/code/Magento/Ui/view/base/web/templates/modal/modal-prompt-content.html create mode 100644 app/code/Magento/Ups/etc/di.xml create mode 100644 app/code/Magento/UrlRewrite/Model/MergeDataProvider.php create mode 100644 app/code/Magento/UrlRewrite/Setup/UpgradeData.php create mode 100644 app/code/Magento/UrlRewrite/Test/Unit/Model/MergeDataProviderTest.php create mode 100644 app/code/Magento/UrlRewrite/Test/Unit/Model/UrlRewriteTest.php create mode 100644 app/code/Magento/UrlRewrite/Test/Unit/Service/V1/Data/UrlRewriteTest.php create mode 100644 app/code/Magento/User/Test/Unit/Controller/Adminhtml/User/DeleteTest.php create mode 100644 app/code/Magento/User/view/adminhtml/web/js/delete-user-account.js create mode 100644 app/code/Magento/Usps/etc/di.xml create mode 100644 app/code/Magento/Vault/Api/PaymentMethodListInterface.php create mode 100644 app/code/Magento/Vault/Model/PaymentMethodList.php create mode 100644 app/code/Magento/Vault/Plugin/PaymentVaultConfigurationProcess.php create mode 100644 app/code/Magento/Vault/Test/Unit/Model/PaymentMethodListTest.php create mode 100644 app/code/Magento/Vault/Test/Unit/Plugin/PaymentVaultConfigurationProcessTest.php create mode 100644 app/code/Magento/Webapi/Test/Unit/Model/ConfigTest.php delete mode 100644 app/code/Magento/Webapi/Test/Unit/Model/Soap/ConfigTest.php delete mode 100644 app/code/Magento/Webapi/Test/Unit/_files/test_interfaces.php create mode 100644 app/code/Magento/Widget/Model/ResourceModel/Widget/Instance/Options/Themes.php create mode 100644 app/code/Magento/Widget/Setup/UpgradeData.php create mode 100644 app/code/Magento/Widget/Test/Unit/Model/ResourceModel/Widget/Instance/Options/ThemesTest.php create mode 100644 app/code/Magento/Wishlist/Setup/UpgradeData.php rename app/code/Magento/Wishlist/view/frontend/web/{ => js}/wishlist.js (80%) create mode 100644 app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less create mode 100644 dev/tests/api-functional/testsuite/Magento/Analytics/Api/LinkProviderTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryMultiCurrencyTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/Catalog/Api/SpecialPriceStorageTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/SalesInventory/Api/Service/V1/ReturnItemsAfterRefundOrderTest.php create mode 100644 dev/tests/functional/etc/repository_replacer_payments.xml create mode 100644 dev/tests/functional/lib/Magento/Mtf/App/State/StateHandlerInterface.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Config/FileResolver/ScopeConfig.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/AdminAnalyzer.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/ConfigAnalyzer.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/Configuration.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/GlobalAnalyzer.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/Helper/UrlAnalyzer.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/HtaccessAnalyzer.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/PhpUnitAnalyzer.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/SeleniumSessionAnalyzer.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/StaticClassesGenerator.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Troubleshooting/StorefrontAnalyzer.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Indexer.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Setup.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/StaticContent.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Data.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/File/ExportInterface.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Filesystem/FileHelper.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Generate/File/Generator.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Generate/File/TemplateInterface.php create mode 100644 dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/Constraint/AssertImportCheckDataErrorMessagesList.php create mode 100644 dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ImportDataNegativeTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/_files/template/pricing/advanced_incorrect.php create mode 100644 dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Mtf/App/State/NotificationTimeHandler.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Block/Adminhtml/Dashboard/Analytics/SubscriptionBlock.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Block/Adminhtml/Dashboard/Page/Actions.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Block/System/Config/AnalyticsForm.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertAcceptSubscriptionPopup.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertBasicTierLink.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsDisabled.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsEnabled.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsRestored.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsVerticalScope.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertDeclineSubscriptionPopup.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertEmptyVerticalCanNotBeSaved.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertFreeTierLink.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertSkipSubscriptionPopup.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertSubscriptionPopupNotExist.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertVerticalIsSet.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Page/Adminhtml/ConfigAnalytics.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Page/Adminhtml/Dashboard.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Repository/Role.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/Repository/User.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/AnalyticsSubscriptionCheckPermissionsTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/AnalyticsSubscriptionCheckPermissionsTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/FreeTierButtonTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/FreeTierButtonTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/InstallTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/SetVerticalTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/SetVerticalTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/TestStep/OpenAnalyticsConfigStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/Analytics/Test/etc/testcase.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Form/Cc.php delete mode 100755 dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Form/Cc.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Authorizenet/Test/Fixture/CreditCardAuthorizenet.xml create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Tabs.php create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertDeveloperSectionVisibility.php create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertLocaleCodeVisibility.php create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertMenuItemNotVisible.php create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigPageVisibilityTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigPageVisibilityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Backend/Test/TestStep/OpenDashboardStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/BraintreeCc.php rename dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/{Cc.xml => BraintreeCc.xml} (53%) delete mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Cc.php delete mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Info.php create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertCreditCardJsValidationMessagesArePresent.php create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php rename dev/tests/functional/tests/app/Magento/Braintree/Test/Fixture/{CreditCardBraintree.xml => BraintreeSandboxCustomer.xml} (50%) delete mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/Page/Adminhtml/SalesOrderView.xml create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/BraintreeSandboxCustomer.xml create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOnlineCreditMemoBraintreeTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOnlineCreditMemoBraintreeTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderWithPayPalBraintreeVaultBackendTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderWithPayPalBraintreeVaultBackendTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutDeclinedTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutFromMiniShoppingCartTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWith3dSecureFailedTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWith3dSecureFailedTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/VerifyPaymentMethodOnCheckoutTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestStep/PlaceOrderWith3dSecureFailedStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Braintree/Test/TestStep/SettleTransactionStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Catalog/Product/View/Summary.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Catalog/Product/View/Summary/ConfiguredPrice.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Catalog/Product/View/Type/Option/Element/Qty.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Catalog/Product/View/Type/Option/Hidden.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Catalog/Product/View/Type/Option/Hidden.xml create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleItemsSummaryOnProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleOptionTitleOnStorefront.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleOptionsDeleted.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundlePriceCalculatedOnProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/BundleOptionsSummaryTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/BundleOptionsSummaryTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleOptionsTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleOptionsTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Bundle/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Block/Adminhtml/LoginWithCaptcha.php create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Block/Adminhtml/LoginWithCaptcha.xml create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Block/Form/LoginWithCaptcha.php create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Block/Form/LoginWithCaptcha.xml create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Constraint/AssertCaptchaFieldOnBackend.php create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Constraint/AssertCaptchaFieldOnStorefront.php create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Fixture/Customer.xml create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Fixture/User.xml create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Page/Adminhtml/AdminAuthLoginWithCaptcha.xml create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Page/CustomerAccountLogin.xml create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/Repository/ConfigData.xml create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/TestCase/CaptchaOnAdminLoginTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/TestCase/CaptchaOnAdminLoginTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/TestCase/CaptchaOnStoreFrontLoginTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Captcha/Test/TestCase/CaptchaOnStoreFrontLoginTest.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Attribute.xml create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Tab/UpdateAttributeTab.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/UpdateAttributeForm.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/UpdateAttributeForm.xml create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/BlockGallery.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertAdvancedPriceAbsentOnProductForm.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCanSaveProduct.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryInNavigationMenu.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryMovedMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryOnCustomStore.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryOnCustomWebsite.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductHasImageInGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCategoryOnCustomWebsite.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInCustomStoreView.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMaxAllowedQty.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMinAllowedQty.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryThreshold.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductNameOnDifferentStoreViews.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductPriceOnDifferentStoreViews.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductTierPriceInCart.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductsInStock.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductsQtyAndStockStatusInAdminPanel.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/Image.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple/WebsiteData.xml create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityFlatDataTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityFlatDataTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateTopCategoryEntityTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateTopCategoryEntityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityByAttributeMaskSkuTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityByAttributeMaskSkuTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ManageProductsStockTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ManageProductsStockTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestStep/ConfigureProductOnProductPageStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/TestStep/MergePreconditionProductsStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/_files/test1.png create mode 100644 dev/tests/functional/tests/app/Magento/Catalog/Test/_files/test2.png create mode 100644 dev/tests/functional/tests/app/Magento/CatalogInventoryConfigurableProduct/Test/Constraint/AssertOutOfStockOptionIsAbsentOnProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogInventoryConfigurableProduct/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml create mode 100644 dev/tests/functional/tests/app/Magento/CatalogInventoryConfigurableProduct/Test/TestCase/CreateConfigurableProductEntityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/CatalogInventoryConfigurableProduct/Test/etc/di.xml rename dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/{AssertCatalogPriceRuleAppliedOnepageCheckout.php => AssertCatalogPriceRuleOnOnepageCheckout.php} (95%) create mode 100644 dev/tests/functional/tests/app/Magento/CatalogRuleConfigurable/Test/TestCase/ApplyConfigurableProductCatalogPriceRulesTest.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogRuleConfigurable/Test/TestCase/ApplyConfigurableProductCatalogPriceRulesTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/SearchResultsTitle.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchAttributeIsAbsent.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchEmptyTerm.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchProductResult.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertCatalogSearchQueryLength.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertCatalogSearchResultOrder.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertProductAddedToCartFromSearchResults.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchAttributeTest.php create mode 100644 dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml create mode 100644 dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/Fixture/Category.xml create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Pager.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/CustomAddress.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/AddressModal.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/AddressModal.xml create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/ShippingPopup.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/ShippingPopup.xml create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonAbsentOnCategoryPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonAbsentOnProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonPresentOnCategoryPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonPresentOnProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertBillingAddressAbsentInPayment.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCancelSuccessMessageInShoppingCart.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCartPerCustomer.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCheckoutErrorMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCustomerIsRedirectedToCheckoutFromCart.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertEmailErrorValidationMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertEmailToolTips.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertItemsCounterInMiniShoppingCart.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertLinkGoToCartNotPresentInSummaryBlock.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertLinkGoToCartPresentInSummaryBlock.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertMinicartItemsQty.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertMyCartLinkRedirect.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPagersNotPresentInShoppingCart.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPagersPresentInShoppingCart.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPagersSummaryText.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPaymentMethodIsAbsentOnPaymentPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertShippingAddressJsValidationMessagesIsAbsent.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertTopDestinationsInSelect.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertVisibleItemsQtyInCheckoutSummaryBlock.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertVisibleItemsQtyInMiniShoppingCart.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertVisibleItemsQtyMessageInMiniShoppingCart.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertVisibleItemsQtyMessageOnCheckoutSummaryBlock.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Repository/ConfigData.xml create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutDeclinedTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutFromMiniShoppingCartTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutJsValidationTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutJsValidationTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPerCustomerTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPerCustomerTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/VerifyPaymentMethodOnCheckoutTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/AddNewShippingAddressStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/ClickPlaceOrderButtonStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/GetPlacedOrderIdStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/ProceedToCheckoutFromMiniShoppingCartStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/RemoveProductsFromTheCartStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/Block/Messages.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsPageFormSingleStoreMode.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsPageOnFrontend.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsPagesDisabledOnFrontend.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsPagesInGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsPagesOnFrontendMultipleStoreViews.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/Fixture/CmsPage/StoreId.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CmsPageMassActionTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CmsPageMassActionTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityMultipleStoreViewsTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCmsPageEntityMultipleStoreViewsTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Grid.php create mode 100644 dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductsQtyAfterReorder.php create mode 100644 dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertCurrencyRateAppliedOnProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertProductQtyDecreasedAfterCreditmemo.php create mode 100644 dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertProductTierPriceOnProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCreditMemoEntityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCurrencyRateTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/MassProductUpdateTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Constraint/AssertCurrencySymbolOnProductPageCustomWebsite.php create mode 100644 dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Constraint/AssertCurrencySymbolOnProductPageMainWebsite.php create mode 100644 dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/TestCase/EditCurrencyCustomWebsiteTest.php create mode 100644 dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/TestCase/EditCurrencyCustomWebsiteTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/Block/Account/AuthenticationWrapper.php create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/Block/Account/AuthenticationWrapper.xml create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerLoginErrorMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerPasswordAutocompleteOnAuthorizationPopup.php create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerPasswordAutocompleteOnSignIn.php create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/Fixture/Customer/WebsiteId.php create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/Page/Adminhtml/CheckoutIndex.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/Page/CustomerAddressEdit.php create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/Page/CustomerAddressEdit.xml create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Dhl/Test/TestCase/CityBasedShippingRateTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Directory/Test/Constraint/AssertCurrencyRateAppliedOnProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Directory/Test/Constraint/AssertShippingPriceWithCustomCurrency.php create mode 100644 dev/tests/functional/tests/app/Magento/Directory/Test/Repository/ConfigData.xml create mode 100644 dev/tests/functional/tests/app/Magento/Fedex/Test/TestCase/CityBasedShippingRateTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/GiftMessage/Test/TestCase/CreateGiftMessageOnBackendTest.php create mode 100644 dev/tests/functional/tests/app/Magento/GiftMessage/Test/TestCase/CreateGiftMessageOnBackendTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Constraint/AbstractAssertTaxRuleIsAppliedToAllPricesOnGroupedProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Constraint/AssertProductInItemsOrderedGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Constraint/AssertTaxRuleIsAppliedToAllPricesGroupedExcludingIncludingTax.php create mode 100644 dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/TaxCalculationTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/GroupedProduct/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Mtf/Util/Import/File/CsvTemplate.php create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/Block/Adminhtml/Import/Edit/Form.php create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/Block/Adminhtml/Import/Edit/Form.xml create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/Block/Adminhtml/Import/FormPageActions.php create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/Block/Adminhtml/Import/Frame/Result.php create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/Constraint/AssertImportCheckDataErrorMessage.php rename dev/tests/functional/tests/app/Magento/ImportExport/Test/Fixture/{ImportExport.xml => ExportData.xml} (78%) create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/Fixture/Import/File.php create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/Fixture/ImportData.xml create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/Page/Adminhtml/AdminImportIndex.xml rename dev/tests/functional/tests/app/Magento/ImportExport/Test/Repository/{ImportExport.xml => ExportData.xml} (77%) create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/TestCase/ImportDataNegativeTest.php create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/TestStep/ClickCheckDataStep.php create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/TestStep/FillImportFormStep.php create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/TestStep/OpenImportIndexStep.php create mode 100644 dev/tests/functional/tests/app/Magento/ImportExport/Test/etc/testcase.xml create mode 100644 dev/tests/functional/tests/app/Magento/Indexer/Test/Block/Adminhtml/IndexManagement/Grid.php create mode 100644 dev/tests/functional/tests/app/Magento/Indexer/Test/Constraint/AssertIndexerStatus.php create mode 100644 dev/tests/functional/tests/app/Magento/Indexer/Test/Constraint/AssertUpdateByScheduleSuccessSaveMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Indexer/Test/Page/Adminhtml/IndexManagement.xml create mode 100644 dev/tests/functional/tests/app/Magento/Indexer/Test/TestCase/CreateCatalogRulesIndexerTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Indexer/Test/TestCase/CreateCatalogRulesIndexerTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Install/Test/Block/Devdocs.php create mode 100644 dev/tests/functional/tests/app/Magento/Install/Test/Constraint/AssertDevdocsLink.php create mode 100644 dev/tests/functional/tests/app/Magento/Install/Test/Constraint/AssertGenerationFilePathCheck.php create mode 100644 dev/tests/functional/tests/app/Magento/Install/Test/Page/DevdocsInstall.xml create mode 100644 dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/Constraint/AssertCategorySortingOnFilteredProductList.php create mode 100644 dev/tests/functional/tests/app/Magento/Newsletter/Test/Repository/Customer.xml create mode 100644 dev/tests/functional/tests/app/Magento/PageCache/Test/Constraint/AssertCategoryCaching.php create mode 100644 dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheInvalidationTest.php create mode 100644 dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheInvalidationTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheStatusOnScheduledIndexingTest.php create mode 100644 dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheStatusOnScheduledIndexingTest.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Payment/Test/Block/Form/Cc.php rename dev/tests/functional/tests/app/Magento/{Catalog/Test/Block/Adminhtml/Product/Edit/Action/Attribute.php => Payment/Test/Block/Form/PaymentCc.php} (53%) rename dev/tests/functional/tests/app/Magento/Payment/Test/Block/Form/{Cc.xml => PaymentCc.xml} (87%) create mode 100644 dev/tests/functional/tests/app/Magento/Payment/Test/Constraint/AssertCardRequiredFields.php delete mode 100644 dev/tests/functional/tests/app/Magento/Payment/Test/Fixture/CreditCardAdmin.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Payment/Test/Repository/CreditCardAdmin.xml create mode 100644 dev/tests/functional/tests/app/Magento/Payment/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Form/HostedPro/Cc.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Form/HostedPro/Cc.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Form/PayflowLink/Cc.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Form/PayflowLink/Cc.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Form/PaymentsAdvanced/Cc.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Form/PaymentsAdvanced/Cc.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Onepage/Payment/HostedPro.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Onepage/Payment/PayflowLink.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Onepage/Payment/PaymentsAdvanced.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Block/Onepage/Payment/PaypalIframe.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/Page/CheckoutOnepage.xml rename dev/tests/functional/tests/app/Magento/{Authorizenet => Paypal}/Test/Repository/CreditCard.xml (53%) create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/CloseOrderTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/CloseSalesWithHostedProTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/CloseSalesWithHostedProTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/CreateOnlineCreditMemoTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/CreatePayFlowOrderBackendNegativeTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/CreatePayFlowOrderBackendNegativeTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/NavigateMenuTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/OnePageCheckoutDeclinedTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/OnePageCheckoutHostedProTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/OnePageCheckoutHostedProTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/OnePageCheckoutPayflowLinkTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/OnePageCheckoutPayflowLinkTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/OnePageCheckoutPaymentsAdvancedTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/OnePageCheckoutPaymentsAdvancedTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/OnePageCheckoutTest.xml delete mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestStep/GetPlacedOrderIdStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestStep/PlaceOrderWithHostedProStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestStep/PlaceOrderWithPayflowLinkStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/TestStep/PlaceOrderWithPaymentsAdvancedStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Paypal/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/Persistent/Test/Constraint/AssertCustomerIsRedirectedToCheckout.php create mode 100644 dev/tests/functional/tests/app/Magento/Persistent/Test/Repository/ConfigData.xml create mode 100644 dev/tests/functional/tests/app/Magento/Persistent/Test/TestCase/CheckoutWithPersistentShoppingCartTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Persistent/Test/TestCase/CheckoutWithPersistentShoppingCartTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/Shipping/Grid.php create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AbstractAssertShippingReportResult.php create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertLifetimeStatisticsUpdatedMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertRecentStatisticsUpdatedMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertReportsUpdatedTimezone.php create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertShippingReportIntervalResult.php create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertShippingReportTotalResult.php create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/Page/Adminhtml/SalesShippingReport.xml create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/RefreshReportsStatisticsTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/RefreshReportsStatisticsTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/CustomerActivities/Sidebar/Wishlist.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Address.xml create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Sidebar.php delete mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/History.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Addresses.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Info/CommentsHistoryBlock.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Info/PaymentInfoBlock.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertCartSectionIsEmptyOnBackendOrderPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertCartSectionWithProductsOnBackendOrderPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertInvoiceNotInInvoicesGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertInvoiceStatusInOrdersGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertItemsOrderedSectionContainsProducts.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertItemsOrderedSectionOnBackendOrderIsEmpty.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOnlineInvoiceCannotBeCreated.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderAddresses.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderBillingAndShippingAddressesAreDifferent.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderCommentsHistoryNotifyStatus.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderItemsPagerDisplayedOnFrontend.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderItemsPagerHiddenOnFrontend.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderPaymentInformation.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderStatusIsCanceled.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderSuccessVoidedMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductInCustomerShoppingCartOnBackendGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductQtyDecreased.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductQtyDecreasedAfterCreditmemo.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductsQtyAfterOrderCancel.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertRefundNotInRefundsGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertRefundOrderStatusInCommentsHistory.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertReorderButtonIsNotVisibleOnFrontend.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertTransactionStatus.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertVoidInCommentsHistory.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CloseOrderTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOnlineCreditMemoTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderFromEditCustomerPageTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderFromEditCustomerPageTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/FrontendOrderPagerTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/FrontendOrderPagerTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/VoidAuthorizationTest.php rename dev/tests/functional/tests/app/Magento/{Braintree/Test/TestStep/CreateBraintreeCreditMemoStep.php => Sales/Test/TestStep/CreateOnlineCreditMemoStep.php} (71%) create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/FillShippingAddressStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/SubmitOrderNegativeStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/VoidAuthorizationStep.php create mode 100644 dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/Section/ManageCouponCode.php create mode 100644 dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ShoppingCartWithFreeShippingTest.php create mode 100644 dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ShoppingCartWithFreeShippingTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Search/Test/Constraint/AssertSynonymGroupsSearch.php create mode 100644 dev/tests/functional/tests/app/Magento/Search/Test/Constraint/AssertSynonymRestrictedAccess.php create mode 100644 dev/tests/functional/tests/app/Magento/Search/Test/TestCase/AdvancedSearchWithAttributeTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Search/Test/TestCase/AdvancedSearchWithAttributeTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Search/Test/TestCase/CreateMultipleSynonymGroupsTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Search/Test/TestCase/CreateMultipleSynonymGroupsTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Search/Test/TestCase/CustomAclPermissionTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Search/Test/TestStep/DeleteAllSynonymGroupsStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertCustomerEmailChanged.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertCustomerPasswordRequiredClasses.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertDefaultAccountInformation.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingIntegrationTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingIntegrationTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingRoleTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingRoleTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingUserTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingUserTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/RegisterCustomerEntityWithDifferentPasswordClassesTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/RegisterCustomerEntityWithDifferentPasswordClassesTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerEmailTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Security/Test/TestCase/SecureChangingCustomerPasswordTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php create mode 100644 dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertShipmentNotInShipmentsGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CityBasedShippingRateTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/SalesShippingReportEntityTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/SalesShippingReportEntityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Shipping/Test/TestStep/FillShippingAddressesStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Shipping/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/Shipping/Test/etc/testcase.xml create mode 100644 dev/tests/functional/tests/app/Magento/Sitemap/Test/Constraint/AssertSitemapSubmissionToRobotsTxt.php create mode 100644 dev/tests/functional/tests/app/Magento/Sitemap/Test/Repository/ConfigData.xml create mode 100644 dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/GenerateSitemapEntityTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/GenerateSitemapEntityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/UpdateSitemapEntityTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Sitemap/Test/TestCase/UpdateSitemapEntityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreCodeInUrl.php create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreDisabledErrorSaveMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreGroupNoDeleteButton.php create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreNoDeleteButton.php create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/Repository/ConfigData.xml create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/TestCase/MoveStoreToOtherGroupSameWebsiteTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/TestCase/MoveStoreToOtherGroupSameWebsiteTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Store/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ListProduct.php create mode 100755 dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ProductList/ProductItem.php create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Block/Product/ViewWithSwatches.php create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Constraint/AssertSwatchConfigurableProductPage.php create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Fixture/Cart/Item.php create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Fixture/ConfigurableProduct.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Fixture/SwatchProductAttribute.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Handler/SwatchProductAttribute/Curl.php create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Handler/SwatchProductAttribute/SwatchProductAttributeInterface.php create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Page/Category/CatalogCategoryView.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Page/Product/CatalogProductView.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Repository/ConfigurableProduct.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Repository/ConfigurableProduct/CheckoutData.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/Repository/SwatchProductAttribute.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShoppingCartTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShoppingCartTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/TestStep/AddProductToCartFromCatalogCategoryPageStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Swatches/Test/etc/curl/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/Theme/Test/Block/Html/Logo.php create mode 100644 dev/tests/functional/tests/app/Magento/Ui/Test/Block/Messages.php create mode 100644 dev/tests/functional/tests/app/Magento/Ui/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertCategoryUrlWithCustomStoreView.php create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteAfterDeletingCategory.php create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductInGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductNotInGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteRedirectInGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewritesCategoriesInGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewritesRedirectInGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CategoryUrlRewriteTest.php create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CategoryUrlRewriteTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.php create mode 100644 dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/Block/Adminhtml/LockedUsersGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserPasswordChangedSuccessfully.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/Page/Adminhtml/UserLocks.xml create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/TestCase/CustomAclPermissionTest.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdatePasswordUserEntityPciRequirementsTest.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdatePasswordUserEntityPciRequirementsTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/TestStep/CreateUserStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Variable/Test/Constraint/AssertCustomVariableRestrictedAccess.php create mode 100644 dev/tests/functional/tests/app/Magento/Variable/Test/TestCase/CustomAclPermissionTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Vault/Test/Block/Onepage/Payment/Method/Vault.php delete mode 100644 dev/tests/functional/tests/app/Magento/Vault/Test/Block/VaultPayment.php create mode 100644 dev/tests/functional/tests/app/Magento/Vault/Test/Constraint/AssertSaveCreditCardOptionNotPresent.php create mode 100644 dev/tests/functional/tests/app/Magento/Vault/Test/TestStep/CheckSaveCreditCardOptionStep.php create mode 100644 dev/tests/functional/tests/app/Magento/Vault/Test/etc/di.xml create mode 100644 dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertThemeFilterValuesOnWidgetGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetsInGrid.php create mode 100644 dev/tests/functional/tests/app/Magento/Widget/Test/TestCase/CreateWidgetsEntityTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Widget/Test/TestCase/CreateWidgetsEntityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertCustomerWishlistOnBackendIsEmpty.php create mode 100644 dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductPriceIsNotZero.php create mode 100644 dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductsIsPresentInCustomerBackendWishlist.php create mode 100644 dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/basic_green.xml create mode 100644 dev/tests/functional/utils/deleteMagentoGeneratedCode.php create mode 100644 dev/tests/functional/utils/export.php create mode 100644 dev/tests/functional/utils/log.php create mode 100644 dev/tests/functional/utils/mtf create mode 100644 dev/tests/functional/utils/pathChecker.php create mode 100644 dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/etc/module.xml create mode 100644 dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/etc/zip_codes.xml create mode 100644 dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/registration.php create mode 100644 dev/tests/integration/framework/Magento/TestFramework/App/Config.php create mode 100644 dev/tests/integration/framework/Magento/TestFramework/App/MutableScopeConfig.php create mode 100644 dev/tests/integration/framework/Magento/TestFramework/App/ReinitableConfig.php create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Backend/App/Config.php create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Helper/CacheCleaner.php create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Isolation/AppConfig.php create mode 100644 dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/App/ConfigTest.php create mode 100644 dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Isolation/AppConfigTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Analytics/_files/create_link.php create mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Controller/Directpost/Payment/ResponseTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php create mode 100644 dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/Tab/Products/ViewedTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/Column/Renderer/TextTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Gateway/Config/ConfigTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Model/Adminhtml/System/Config/CountryCreditCardTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Model/PaymentMethodListTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Model/Ui/Adminhtml/PayPal/TokenUiComponentProviderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/_files/payments.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/BundlePriceAbstract.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundleWithCatalogPriceRuleCalculatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundleWithSpecialPriceCalculatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundleWithTierPriceCalculatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithCatalogPriceRuleCalculatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithSpecialPriceCalculatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithTierPriceCalculatorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Model/Product/IsSaleableTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_catalog_rule.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_catalog_rule_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_special_price.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_special_price_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product_with_catalog_rule.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product_with_catalog_rule_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product_with_special_price.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product_with_special_price_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/issaleable_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/issaleable_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Cron/DeleteOutdatedPriceValuesTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Category/CategoryImageTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Category/CategoryImageTest/StubZendLogWriterStream.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/ConfigTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Source/CountryofmanufactureTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/DateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChangeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Pricing/Render/FinalPriceBoxTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/expected_categories.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/input_meta_for_categories.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_renamed_group.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/enable_price_index_schedule.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/enable_price_index_schedule_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_incorrect_values.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_different_store_prices.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_different_store_prices_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_has_tier_price_show_as_low_as.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_has_tier_price_show_as_low_as_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/second_website_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_update_label.csv create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_additional_attributes.csv create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_categories.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_product_links_data_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogInventory/Api/StockItemSaveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogInventory/Model/System/Config/Backend/MinsaleqtyTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/Model/ResourceModel/Rule/CollectionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/IndexSwitcherMock.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/SwitcherUsedInFulltextTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserverTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/Block/Cart/GridTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigShowCommandTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Config/Model/Config/Processor/EnvironmentPlaceholderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Config/_files/_config.local.php create mode 100644 dev/tests/integration/testsuite/Magento/Config/_files/_config.php create mode 100644 dev/tests/integration/testsuite/Magento/Config/_files/config_data.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/Import/_files/import_configurable_12345.csv create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/ConfigurableTest.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionProviderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_12345.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_12345_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Contact/Model/ConfigTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Block/Form/RegisterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Block/Widget/CompanyTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Block/Widget/FaxTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Block/Widget/TelephoneTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_website.php create mode 100644 dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ApplicationDumpCommandTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommandTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Deploy/_files/_config.local.php create mode 100644 dev/tests/integration/testsuite/Magento/Deploy/_files/config_data.php create mode 100644 dev/tests/integration/testsuite/Magento/Directory/Block/DataTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/Config/ReaderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Downloadable/_files/order_with_downloadable_product_with_additional_options.php create mode 100644 dev/tests/integration/testsuite/Magento/Eav/Model/AttributeManagementTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Eav/Model/ConfigTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Eav/Model/ResourceModel/UpdateHandlerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/App/Config/InitialTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/App/ObjectManager/ConfigLoaderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/App/Route/ConfigTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/App/View/Deployment/VersionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Backup/FilesystemTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Backup/_files/var/backups/1474538269_filesystem_code.tgz create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Communication/_files/invalid_communication_numeric.xml create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_numeric.xml create mode 100644 dev/tests/integration/testsuite/Magento/Framework/DB/DataConverter/DataConverterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/Factory/AbstractFactoryRuntimeDefinitionsTestCases.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/Factory/CompiledTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/Factory/Dynamic/DeveloperTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/BasicAlias.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/ComplexDependencies.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/DependsOnAlias.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/DependsOnInterface.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/ObjectManager/TestAsset/HasOptionalParameters.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Reflection/MethodsMapTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/configurable_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/configurable_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_weight_products.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_weight_products_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/Element/UiComponent/Config/Provider/TemplateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/_files/UiComponent/expected/config.xml create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/_files/UiComponent/theme/Magento_Catalog/ui_component/test.xml create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/_files/UiComponent/theme/Magento_Customer/ui_component/test.xml create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/_files/UiComponent/theme/registration.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/_files/UiComponent/theme/theme.xml create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/_files/layout_directives_test/remove_cancellation.xml create mode 100644 dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates.php create mode 100644 dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/PageCache/Model/ConfigTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PageCache/Model/Layout/MergeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PageCache/Model/_files/result.vcl create mode 100644 dev/tests/integration/testsuite/Magento/PageCache/Model/_files/test.vcl create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/_files/quote_express.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/_files/quote_express_with_customer.php create mode 100644 dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/QuoteTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Quote/_files/is_not_salable_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Quote/_files/is_salable_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Block/Order/ItemsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Model/Order/Address/RendererTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreditmemoFactoryTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Model/Order/ItemTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_fixture_store_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_dummy_item_and_invoiced.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_shipping_and_invoice_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/Model/Quote/Address/Total/ShippingTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/RuleTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/rule_custom_product_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/rule_free_shipping_by_product_weight.php create mode 100644 dev/tests/integration/testsuite/Magento/SampleData/Model/DependencyTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/FirstModule/composer.json create mode 100644 dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/SecondModule/composer.json create mode 100644 dev/tests/integration/testsuite/Magento/SampleData/_files/Modules/ThirdModule/composer.json create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Fixtures/FixturesAsserts/BundleProductsAssert.php create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Fixtures/FixturesAsserts/ConfigurableProductsAssert.php create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Fixtures/FixturesAsserts/ProductAssert.php create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Fixtures/FixturesAsserts/SimpleProductsAssert.php create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Fixtures/_files/attributeSets.xml create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Model/FixtureGenerator/ProductGeneratorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/Model/StoreManagerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/Model/StoreResolverTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/scope.config.fixture.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_second_currency.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_second_currency_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_two_stores.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_two_stores_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/Model/AttributeCreateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Theme/Model/Theme/ThemeProviderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Vault/_files/token.php create mode 100644 dev/tests/integration/testsuite/Magento/Webapi/Model/ServiceMetadataTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Webapi/Model/Soap/ConfigTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Widget/_files/new_widget.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Model/ItemTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Analytics/adminhtml/js/modal/modal-component.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/components/dynamic-rows-import-custom-options.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/utils/percentage-price-calculator.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/cache.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/new-customer-address.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/cart/shipping-estimation.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/minicart.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/summary/cart-items.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/model/customer/address.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/view/authentication-popup.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Review/view/frontend/web/js/process-review.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/date-time.test.js delete mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/controls/bookmarks/bookmarks.test.js delete mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/controls/bookmarks/view.test.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js delete mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/component/core.test.js delete mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/component/manip.test.js delete mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/component/provider.test.js delete mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/component/traversal.test.js delete mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/registry/events.test.js delete mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/registry/storage.test.js delete mode 100644 dev/tests/js/jasmine/tests/lib/mage/gallery.test.js create mode 100644 dev/tests/js/jasmine/tests/lib/mage/misc.test.js create mode 100644 dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/FinalImplementation.php create mode 100644 dev/tests/static/framework/Magento/CodeMessDetector/Test/Unit/Rule/Design/FinalImplementationTest.php create mode 100644 dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml create mode 100644 dev/tests/static/framework/Magento/TestFramework/Utility/File.php create mode 100644 dev/tests/static/framework/Magento/TestFramework/Utility/File/RegexIteratorFactory.php create mode 100644 dev/tests/static/framework/Magento/TestFramework/Utility/FunctionDetector.php create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/ConstantUsageSniffTest.php create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/_files/correct_arguments.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/_files/incorrect_arguments.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/CodeCheckTest.php create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/class_name_in_namespace_and_variable_name.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/create_new_instance.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/create_new_instance2.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/create_new_instance3.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/extends.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/extends2.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/implements.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/implements2.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/use.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/Test/Utility/File/_files/use2.txt create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FileTest.php create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FunctionDetectorTest.php create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/test.txt delete mode 100644 dev/tests/static/testsuite/Magento/Test/Integrity/ApiAnnotationTest.php create mode 100644 dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/observers.txt create mode 100644 dev/tests/static/testsuite/Magento/Test/Legacy/UnsecureFunctionsUsageTest.php create mode 100644 dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/unsecure_php_functions.php create mode 100644 dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/whitelist.txt create mode 100644 generated/.htaccess create mode 100644 lib/internal/Magento/Framework/Acl/Data/Cache.php create mode 100644 lib/internal/Magento/Framework/Acl/Data/CacheInterface.php create mode 100644 lib/internal/Magento/Framework/Api/Test/Unit/Search/SearchResultTest.php create mode 100644 lib/internal/Magento/Framework/App/Cache/Tag/Resolver.php create mode 100644 lib/internal/Magento/Framework/App/Cache/Tag/Strategy/Dummy.php create mode 100644 lib/internal/Magento/Framework/App/Cache/Tag/Strategy/Factory.php create mode 100644 lib/internal/Magento/Framework/App/Cache/Tag/Strategy/Identifier.php create mode 100644 lib/internal/Magento/Framework/App/Cache/Tag/StrategyInterface.php create mode 100644 lib/internal/Magento/Framework/App/Cache/Type/Dummy.php create mode 100644 lib/internal/Magento/Framework/App/Config/CommentInterface.php create mode 100644 lib/internal/Magento/Framework/App/Config/CommentParserInterface.php create mode 100644 lib/internal/Magento/Framework/App/Config/ConfigPathResolver.php create mode 100644 lib/internal/Magento/Framework/App/Config/ConfigSourceAggregated.php create mode 100644 lib/internal/Magento/Framework/App/Config/ConfigSourceInterface.php create mode 100644 lib/internal/Magento/Framework/App/Config/ConfigTypeInterface.php create mode 100644 lib/internal/Magento/Framework/App/Config/InitialConfigSource.php create mode 100644 lib/internal/Magento/Framework/App/Config/MetadataConfigTypeProcessor.php create mode 100644 lib/internal/Magento/Framework/App/Config/PostProcessorComposite.php create mode 100644 lib/internal/Magento/Framework/App/Config/PreProcessorComposite.php create mode 100644 lib/internal/Magento/Framework/App/Config/Reader/Source/SourceInterface.php delete mode 100644 lib/internal/Magento/Framework/App/Config/Scope/ReaderPoolInterface.php create mode 100644 lib/internal/Magento/Framework/App/Config/Scope/Validator.php create mode 100644 lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php delete mode 100644 lib/internal/Magento/Framework/App/Config/ScopePool.php create mode 100644 lib/internal/Magento/Framework/App/Config/Spi/PostProcessorInterface.php create mode 100644 lib/internal/Magento/Framework/App/Config/Spi/PreProcessorInterface.php create mode 100644 lib/internal/Magento/Framework/App/PlainTextRequestInterface.php create mode 100644 lib/internal/Magento/Framework/App/RequestContentInterface.php create mode 100644 lib/internal/Magento/Framework/App/Scope/Validator.php create mode 100644 lib/internal/Magento/Framework/App/Scope/ValidatorInterface.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/ResolverTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/DummyTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/FactoryTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Cache/Tag/Strategy/IdentifierTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Config/ConfigPathResolverTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Config/ConfigSourceAggregatedTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Config/InitialConfigSourceTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Config/MetadataConfigTypeProcessorTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Config/PreProcessorCompositeTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Config/Scope/ValidatorTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Config/ScopeCodeResolverTest.php delete mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Config/ScopePoolTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/ConfigTest.php delete mode 100644 lib/internal/Magento/Framework/App/Test/Unit/ReinitableConfigTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Scope/ValidatorTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/_files/pub/index.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/_files/setup/index.php create mode 100644 lib/internal/Magento/Framework/Config/Reader.php create mode 100644 lib/internal/Magento/Framework/Config/Test/Unit/ReaderTest.php create mode 100644 lib/internal/Magento/Framework/Crontab/CrontabManager.php create mode 100644 lib/internal/Magento/Framework/Crontab/CrontabManagerInterface.php create mode 100644 lib/internal/Magento/Framework/Crontab/README.md create mode 100644 lib/internal/Magento/Framework/Crontab/TasksProvider.php create mode 100644 lib/internal/Magento/Framework/Crontab/TasksProviderInterface.php create mode 100644 lib/internal/Magento/Framework/Crontab/Test/Unit/CrontabManagerTest.php create mode 100644 lib/internal/Magento/Framework/Crontab/Test/Unit/TasksProviderTest.php create mode 100644 lib/internal/Magento/Framework/DB/Adapter/DuplicateException.php create mode 100644 lib/internal/Magento/Framework/DB/DataConverter/DataConversionException.php create mode 100644 lib/internal/Magento/Framework/DB/DataConverter/DataConverterInterface.php create mode 100644 lib/internal/Magento/Framework/DB/DataConverter/SerializedToJson.php create mode 100644 lib/internal/Magento/Framework/DB/FieldDataConversionException.php create mode 100644 lib/internal/Magento/Framework/DB/FieldDataConverter.php create mode 100644 lib/internal/Magento/Framework/DB/FieldDataConverterFactory.php create mode 100644 lib/internal/Magento/Framework/DB/Query/BatchIteratorInterface.php create mode 100644 lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php create mode 100644 lib/internal/Magento/Framework/DB/Select/InQueryModifier.php create mode 100644 lib/internal/Magento/Framework/DB/Select/QueryModifierFactory.php create mode 100644 lib/internal/Magento/Framework/DB/Select/QueryModifierInterface.php create mode 100644 lib/internal/Magento/Framework/DB/Sql/ColumnValueExpression.php create mode 100644 lib/internal/Magento/Framework/DB/TemporaryTableService.php create mode 100644 lib/internal/Magento/Framework/DB/Test/Unit/DataConverter/SerializedToJsonTest.php create mode 100644 lib/internal/Magento/Framework/DB/Test/Unit/FieldDataConverterFactoryTest.php create mode 100644 lib/internal/Magento/Framework/DB/Test/Unit/FieldDataConverterTest.php create mode 100644 lib/internal/Magento/Framework/DB/Test/Unit/Select/QueryModifierFactoryTest.php create mode 100644 lib/internal/Magento/Framework/DB/Test/Unit/TemporaryTableServiceTest.php create mode 100644 lib/internal/Magento/Framework/EntityManager/Observer/BeforeEntityLoad.php create mode 100644 lib/internal/Magento/Framework/EntityManager/Test/Unit/Operation/CreateTest.php create mode 100644 lib/internal/Magento/Framework/EntityManager/Test/Unit/Operation/UpdateTest.php create mode 100644 lib/internal/Magento/Framework/File/Test/Unit/_files/UPPERCASE.WEIRD delete mode 100644 lib/internal/Magento/Framework/Interception/Definition/Compiled.php delete mode 100644 lib/internal/Magento/Framework/Interception/Test/Unit/Definition/CompiledTest.php delete mode 100644 lib/internal/Magento/Framework/ObjectManager/Definition/Compiled.php delete mode 100644 lib/internal/Magento/Framework/ObjectManager/Definition/Compiled/Binary.php delete mode 100644 lib/internal/Magento/Framework/ObjectManager/Definition/Compiled/Serialized.php delete mode 100644 lib/internal/Magento/Framework/ObjectManager/Relations/Compiled.php create mode 100644 lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php delete mode 100644 lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/Compiled/BinaryTest.php delete mode 100644 lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/Compiled/SerializedTest.php delete mode 100644 lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/CompiledStub.php delete mode 100644 lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/CompiledTest.php delete mode 100644 lib/internal/Magento/Framework/ObjectManager/Test/Unit/Relations/CompiledTest.php create mode 100644 lib/internal/Magento/Framework/Serialize/README.md create mode 100644 lib/internal/Magento/Framework/Serialize/Serializer/Json.php create mode 100644 lib/internal/Magento/Framework/Serialize/Serializer/Serialize.php create mode 100644 lib/internal/Magento/Framework/Serialize/SerializerInterface.php create mode 100644 lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonTest.php create mode 100644 lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/SerializeTest.php create mode 100644 lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php create mode 100644 lib/internal/Magento/Framework/Url/HostChecker.php create mode 100644 lib/internal/Magento/Framework/Url/Test/Unit/HostCheckerTest.php create mode 100644 lib/internal/Magento/Framework/Validator/AllowedProtocols.php create mode 100644 lib/internal/Magento/Framework/Validator/Test/Unit/UrlTest.php create mode 100644 lib/internal/Magento/Framework/Validator/Url.php create mode 100644 lib/internal/Magento/Framework/View/Element/Block/ArgumentInterface.php create mode 100644 lib/internal/Magento/Framework/View/EntitySpecificHandlesList.php create mode 100644 lib/internal/Magento/Framework/View/Test/Unit/EntitySpecificHandlesListTest.php create mode 100644 lib/web/moment-timezone-with-data.js create mode 100644 pub/health_check.php create mode 100644 setup/performance-toolkit/config/attributeSets.xml create mode 100644 setup/performance-toolkit/config/customerConfig.xml create mode 100644 setup/performance-toolkit/config/description.xml create mode 100644 setup/performance-toolkit/config/di.xml create mode 100644 setup/performance-toolkit/config/searchConfig.xml create mode 100644 setup/performance-toolkit/config/searchTerms.xml create mode 100644 setup/performance-toolkit/config/searchTermsLarge.xml create mode 100644 setup/performance-toolkit/config/shortDescription.xml create mode 100755 setup/pub/angular-clickout/angular-clickout.js create mode 100755 setup/pub/angular-clickout/angular-clickout.min.js.map create mode 100644 setup/pub/angular-sanitize/angular-sanitize.js create mode 100644 setup/pub/angular-sanitize/angular-sanitize.min.js.map rename {app/code/Magento/Deploy => setup/src/Magento/Setup}/Console/Command/DeployStaticContentCommand.php (93%) delete mode 100644 setup/src/Magento/Setup/Console/Command/DiCompileMultiTenantCommand.php create mode 100644 setup/src/Magento/Setup/Controller/UrlCheck.php create mode 100644 setup/src/Magento/Setup/Fixtures/AdminUsersFixture.php create mode 100644 setup/src/Magento/Setup/Fixtures/AttributeSet/AttributeSetFixture.php create mode 100644 setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php create mode 100644 setup/src/Magento/Setup/Fixtures/AttributeSet/SwatchesGenerator.php create mode 100644 setup/src/Magento/Setup/Fixtures/AttributeSetsFixture.php create mode 100644 setup/src/Magento/Setup/Fixtures/BundleProductsFixture.php create mode 100644 setup/src/Magento/Setup/Fixtures/CategoryResolver.php create mode 100644 setup/src/Magento/Setup/Fixtures/CustomerGroupsFixture.php create mode 100644 setup/src/Magento/Setup/Fixtures/FixtureConfig.php create mode 100644 setup/src/Magento/Setup/Fixtures/PriceProvider.php create mode 100644 setup/src/Magento/Setup/Fixtures/ProductsAmountProvider.php create mode 100644 setup/src/Magento/Setup/Fixtures/TaxRulesFixture.php create mode 100644 setup/src/Magento/Setup/Fixtures/WebsiteCategoryProvider.php create mode 100644 setup/src/Magento/Setup/Fixtures/_files/dictionary.csv create mode 100644 setup/src/Magento/Setup/Fixtures/_files/orders_fixture_data.json rename setup/src/Magento/Setup/Fixtures/{ => _files}/tax_rates.csv (100%) create mode 100644 setup/src/Magento/Setup/Fixtures/_files/tax_rates_small.csv create mode 100644 setup/src/Magento/Setup/Model/Address/AddressDataGenerator.php create mode 100644 setup/src/Magento/Setup/Model/Customer/CustomerDataGenerator.php create mode 100644 setup/src/Magento/Setup/Model/Customer/CustomerDataGeneratorFactory.php create mode 100644 setup/src/Magento/Setup/Model/DataGenerator.php create mode 100644 setup/src/Magento/Setup/Model/DefaultDescriptionGenerator.php create mode 100644 setup/src/Magento/Setup/Model/Description/DescriptionGenerator.php create mode 100644 setup/src/Magento/Setup/Model/Description/DescriptionParagraphGenerator.php create mode 100644 setup/src/Magento/Setup/Model/Description/DescriptionSentenceGenerator.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/BoldMixin.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/BrakeMixin.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/DescriptionMixinInterface.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/HeaderMixin.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/Helper/RandomWordSelector.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/Helper/WordWrapper.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/ItalicMixin.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/MixinFactory.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/ParagraphMixin.php create mode 100644 setup/src/Magento/Setup/Model/Description/Mixin/SpanMixin.php create mode 100644 setup/src/Magento/Setup/Model/Description/MixinManager.php create mode 100644 setup/src/Magento/Setup/Model/DescriptionGeneratorInterface.php create mode 100644 setup/src/Magento/Setup/Model/Dictionary.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/BundleProductGenerator.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/BundleProductTemplateGenerator.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/ConfigurableProductGenerator.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/ConfigurableProductTemplateGenerator.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/CustomerGenerator.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/CustomerTemplateGenerator.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/EntityGenerator.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/ProductGenerator.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/ProductTemplateGeneratorFactory.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/SimpleProductTemplateGenerator.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/SqlCollector.php create mode 100644 setup/src/Magento/Setup/Model/FixtureGenerator/TemplateEntityGeneratorInterface.php create mode 100644 setup/src/Magento/Setup/Model/SearchTermDescriptionGenerator.php create mode 100644 setup/src/Magento/Setup/Model/SearchTermDescriptionGeneratorFactory.php create mode 100644 setup/src/Magento/Setup/Model/SearchTermManager.php rename {app/code/Magento/Deploy => setup/src/Magento/Setup}/Test/Unit/Console/Command/DeployStaticContentCommandTest.php (97%) rename {app/code/Magento/Deploy => setup/src/Magento/Setup}/Test/Unit/Console/Command/FunctionExistMock.php (77%) create mode 100644 setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/AttributeSetFixtureTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/SwatchesGeneratorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSetsFixtureTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Fixtures/CustomerGroupsFixtureTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Fixtures/FixtureConfigTest.php delete mode 100644 setup/src/Magento/Setup/Test/Unit/Fixtures/SimpleProductsFixtureTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Fixtures/TaxRulesFixtureTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Address/AddressDataGeneratorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Customer/CustomerDataGeneratorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/DataGeneratorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/DescriptionGeneratorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/DescriptionParagraphGeneratorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/DescriptionSentenceGeneratorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/BoldMixinTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/BrakeMixinTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/HeaderMixinTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/Helper/RandomWordSelectorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/Helper/WordWrapperTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/ItalicMixinTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/ParagraphMixinTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/SpanMixinTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/Description/MixinManagerTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/DictionaryTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/FixtureGenerator/SqlCollectorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/SearchTermDescriptionGeneratorTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/SearchTermManagerTest.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/_files/dictionary.csv diff --git a/.gitignore b/.gitignore index 9269cefc51fa0..b7f681b292e4d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,9 +23,12 @@ atlassian* /lib/internal/flex/varien/.settings /node_modules /.grunt +/Gruntfile.js +/package.json /pub/media/*.* !/pub/media/.htaccess +/pub/media/analytics/* /pub/media/catalog/* !/pub/media/catalog/.htaccess /pub/media/customer/* @@ -49,3 +52,5 @@ atlassian* !/var/.htaccess /vendor/* !/vendor/.htaccess +/generated/* +!/generated/.htaccess diff --git a/.php_cs b/.php_cs index 5bd1a01537611..4bd705bb09a2f 100644 --- a/.php_cs +++ b/.php_cs @@ -1,6 +1,6 @@ Welcome Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a cutting edge, feature-rich eCommerce solution that gets results. -The installation instructions that used to be here are now published on our GitHub site. Use the information on this page to get started or go directly to the guide. +## Magento system requirements +[Magento system requirements](http://devdocs.magento.com/magento-system-requirements.html) -

New to Magento? Need some help?

-If you're not sure about the following, you probably need a little help before you start installing the Magento software: +## Install Magento +To install Magento, see either: -* Is the Magento software installed already? -* What's a terminal, command prompt, or Secure Shell (ssh)? -* Where's my Magento server and how do I access it? -* What's PHP? -* What's Apache? -* What's MySQL? - -

Step 1: Verify your prerequisites

- -Use the following table to verify you have the correct prerequisites to install the Magento software. - - - - - - - - - - - - - - - - - - - - - - - -
PrerequisiteHow to checkFor more information
Apache 2.2 or 2.4Ubuntu: apache2 -v
- CentOS: httpd -v
Apache
PHP 5.6.x, 7.0.2 or 7.0.6php -vPHP Ubuntu
PHP CentOS
MySQL 5.6.xmysql -u [root user name] -pMySQL
- -

Step 2: Prepare to install

- -After verifying your prerequisites, perform the following tasks in order to prepare to install the Magento software. - -1. Install Composer -2. Clone the Magento repository - -

Step 3: Install and verify the installation

- -1. Update installation dependencies -2. Install Magento: - * Install Magento software using the web interface - * Install Magento software using the command line -2. Verify the installation +* [Magento DevBox](https://magento.com/tech-resources/download), the easiest way to get started with Magento. +* [Installation guide](http://devdocs.magento.com/guides/v2.0/install-gde/bk-install-guide.html)

Contributing to the Magento 2 code base

Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes, optimizations, or just good suggestions. diff --git a/app/autoload.php b/app/autoload.php index b817bafa7fa4c..6fdfcd07fdff1 100644 --- a/app/autoload.php +++ b/app/autoload.php @@ -2,7 +2,7 @@ /** * Register basic autoloader that uses include path * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ use Magento\Framework\Autoload\AutoloaderRegistry; diff --git a/app/bootstrap.php b/app/bootstrap.php index ec60a1708dacc..eed7b6d798ad3 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -1,6 +1,6 @@ = 50600 && PHP_VERSION_ID < 50700 || PHP_VERSION_ID === 70002 || PHP_VERSION_ID === 70004 || PHP_VERSION_ID >= 70006)) { +if (!defined('PHP_VERSION_ID') || !(PHP_VERSION_ID >= 50605 && PHP_VERSION_ID < 50700 || PHP_VERSION_ID === 70002 || PHP_VERSION_ID === 70004 || PHP_VERSION_ID >= 70006)) { if (PHP_SAPI == 'cli') { - echo 'Magento supports PHP 5.6, 7.0.2, 7.0.4, and 7.0.6 or later. ' . + echo 'Magento supports PHP 5.6.5, 7.0.2, 7.0.4, and 7.0.6 or later. ' . 'Please read http://devdocs.magento.com/guides/v1.0/install-gde/system-requirements.html'; } else { echo << -

Magento supports PHP 5.6, 7.0.2, 7.0.4, and 7.0.6 or later. Please read +

Magento supports PHP 5.6.5, 7.0.2, 7.0.4, and 7.0.6 or later. Please read Magento System Requirements. @@ -35,6 +35,17 @@ $mask = file_exists($umaskFile) ? octdec(file_get_contents($umaskFile)) : 002; umask($mask); +if (empty($_SERVER['ENABLE_IIS_REWRITES']) || ($_SERVER['ENABLE_IIS_REWRITES'] != 1)) { + /* + * Unset headers used by IIS URL rewrites. + */ + unset($_SERVER['HTTP_X_REWRITE_URL']); + unset($_SERVER['HTTP_X_ORIGINAL_URL']); + unset($_SERVER['IIS_WasUrlRewritten']); + unset($_SERVER['UNENCODED_URL']); + unset($_SERVER['ORIG_PATH_INFO']); +} + if (!empty($_SERVER['MAGE_PROFILER']) && isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'text/html') !== false @@ -47,3 +58,7 @@ } date_default_timezone_set('UTC'); + +/* Adjustment of precision value for several versions of PHP */ +ini_set('precision', 17); +ini_set('serialize_precision', 17); diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php index 322c61b32a534..a7b11138628a4 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php @@ -2,7 +2,7 @@ /** * Adminhtml AdminNotification Severity Renderer * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php index b370cbd8b1a6f..aa95028cfb4a0 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php @@ -2,7 +2,7 @@ /** * Adminhtml AdminNotification Severity Renderer * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\AdminNotification\Block\Grid\Renderer; diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php index 7d0ccd163b8e0..c348b6cc76101 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php @@ -2,7 +2,7 @@ /** * Adminhtml AdminNotification Severity Renderer * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\AdminNotification\Block\Grid\Renderer; diff --git a/app/code/Magento/AdminNotification/Block/Inbox.php b/app/code/Magento/AdminNotification/Block/Inbox.php index 899d141c9b07f..984de28fd4b94 100644 --- a/app/code/Magento/AdminNotification/Block/Inbox.php +++ b/app/code/Magento/AdminNotification/Block/Inbox.php @@ -2,7 +2,7 @@ /** * Adminhtml AdminNotification inbox grid * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\AdminNotification\Block; diff --git a/app/code/Magento/AdminNotification/Block/System/Messages.php b/app/code/Magento/AdminNotification/Block/System/Messages.php index 223941c6e43f7..db2ecf62f0685 100644 --- a/app/code/Magento/AdminNotification/Block/System/Messages.php +++ b/app/code/Magento/AdminNotification/Block/System/Messages.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/AdminNotification/etc/adminhtml/di.xml b/app/code/Magento/AdminNotification/etc/adminhtml/di.xml index 8986ce73472e5..1be5f99616cc3 100644 --- a/app/code/Magento/AdminNotification/etc/adminhtml/di.xml +++ b/app/code/Magento/AdminNotification/etc/adminhtml/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/etc/adminhtml/events.xml b/app/code/Magento/AdminNotification/etc/adminhtml/events.xml index 07bd1ae5e8e92..9c897af0a2f94 100644 --- a/app/code/Magento/AdminNotification/etc/adminhtml/events.xml +++ b/app/code/Magento/AdminNotification/etc/adminhtml/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/etc/adminhtml/menu.xml b/app/code/Magento/AdminNotification/etc/adminhtml/menu.xml index f64dcbdb51cfd..47c6644b9f369 100644 --- a/app/code/Magento/AdminNotification/etc/adminhtml/menu.xml +++ b/app/code/Magento/AdminNotification/etc/adminhtml/menu.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/etc/adminhtml/routes.xml b/app/code/Magento/AdminNotification/etc/adminhtml/routes.xml index 9ca1f2d4cd1ae..a710049993270 100644 --- a/app/code/Magento/AdminNotification/etc/adminhtml/routes.xml +++ b/app/code/Magento/AdminNotification/etc/adminhtml/routes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/etc/adminhtml/system.xml b/app/code/Magento/AdminNotification/etc/adminhtml/system.xml index 8038b914fd65b..ab0cff5f897c1 100644 --- a/app/code/Magento/AdminNotification/etc/adminhtml/system.xml +++ b/app/code/Magento/AdminNotification/etc/adminhtml/system.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/etc/config.xml b/app/code/Magento/AdminNotification/etc/config.xml index f1a1abb44546b..c088f7b5e11a7 100644 --- a/app/code/Magento/AdminNotification/etc/config.xml +++ b/app/code/Magento/AdminNotification/etc/config.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/etc/di.xml b/app/code/Magento/AdminNotification/etc/di.xml index a5f94685ed9d1..03e415dd3714e 100644 --- a/app/code/Magento/AdminNotification/etc/di.xml +++ b/app/code/Magento/AdminNotification/etc/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/etc/module.xml b/app/code/Magento/AdminNotification/etc/module.xml index 77d9d4877ac0b..8fdfc713293d3 100644 --- a/app/code/Magento/AdminNotification/etc/module.xml +++ b/app/code/Magento/AdminNotification/etc/module.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/registration.php b/app/code/Magento/AdminNotification/registration.php index ab3e6a6170db0..9bd6a540462f7 100644 --- a/app/code/Magento/AdminNotification/registration.php +++ b/app/code/Magento/AdminNotification/registration.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_index.xml b/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_index.xml index 1d3650fa2afcb..1083e32f3c942 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_index.xml +++ b/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/view/adminhtml/layout/default.xml b/app/code/Magento/AdminNotification/view/adminhtml/layout/default.xml index 780e48378a3a4..4ac9f806e8c66 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/layout/default.xml +++ b/app/code/Magento/AdminNotification/view/adminhtml/layout/default.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdminNotification/view/adminhtml/requirejs-config.js b/app/code/Magento/AdminNotification/view/adminhtml/requirejs-config.js index 69739a39dd4e3..c866e796e0ade 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/requirejs-config.js +++ b/app/code/Magento/AdminNotification/view/adminhtml/requirejs-config.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -10,4 +10,4 @@ var config = { toolbarEntry: 'Magento_AdminNotification/toolbar_entry' } } -}; \ No newline at end of file +}; diff --git a/app/code/Magento/AdminNotification/view/adminhtml/templates/notification/window.phtml b/app/code/Magento/AdminNotification/view/adminhtml/templates/notification/window.phtml index 260c7f9be9684..5e0e23246764a 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/templates/notification/window.phtml +++ b/app/code/Magento/AdminNotification/view/adminhtml/templates/notification/window.phtml @@ -1,6 +1,6 @@ getNoticeMessageText(); ?>
getReadDetailsText(); ?> - \ No newline at end of file + diff --git a/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages.phtml b/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages.phtml index cd4a9a88ebe0c..a70a5e97b57c3 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages.phtml +++ b/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/AdminNotification/view/adminhtml/web/js/grid/columns/message.js b/app/code/Magento/AdminNotification/view/adminhtml/web/js/grid/columns/message.js index aa5477ebafcf0..aedf996278834 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/web/js/grid/columns/message.js +++ b/app/code/Magento/AdminNotification/view/adminhtml/web/js/grid/columns/message.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ define([ diff --git a/app/code/Magento/AdminNotification/view/adminhtml/web/js/grid/listing.js b/app/code/Magento/AdminNotification/view/adminhtml/web/js/grid/listing.js index 1fbda8d0196ca..ae17af57fbef4 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/web/js/grid/listing.js +++ b/app/code/Magento/AdminNotification/view/adminhtml/web/js/grid/listing.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/AdminNotification/view/adminhtml/web/system/notification.js b/app/code/Magento/AdminNotification/view/adminhtml/web/system/notification.js index e3d170ef09c57..2628174c1cd25 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/web/system/notification.js +++ b/app/code/Magento/AdminNotification/view/adminhtml/web/system/notification.js @@ -1,7 +1,8 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + define([ 'jquery', 'mage/template', @@ -15,20 +16,22 @@ define([ modalClass: 'modal-system-messages', systemMessageTemplate: '<% _.each(data.items, function(item) { %>' + - '

  • ' + + '
  • ' + '<%= item.text %>' + '
  • ' + '<% }); %>' }, - _create: function() { + /** @inheritdoc */ + _create: function () { this.options.title = $('#message-system-all').attr('title'); this._super(); }, + /** @inheritdoc */ openModal: function (severity) { var superMethod = $.proxy(this._super, this); - //this.modal.options $.ajax({ url: this.options.ajaxUrl, @@ -56,6 +59,8 @@ define([ return this; }, + + /** @inheritdoc */ closeModal: function () { this._super(); } diff --git a/app/code/Magento/AdminNotification/view/adminhtml/web/template/grid/cells/message.html b/app/code/Magento/AdminNotification/view/adminhtml/web/template/grid/cells/message.html index 869842e8ee87b..b6f659885e864 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/web/template/grid/cells/message.html +++ b/app/code/Magento/AdminNotification/view/adminhtml/web/template/grid/cells/message.html @@ -1,8 +1,8 @@
    \ No newline at end of file + html="$col.getLabel($row())"/> diff --git a/app/code/Magento/AdminNotification/view/adminhtml/web/template/grid/listing.html b/app/code/Magento/AdminNotification/view/adminhtml/web/template/grid/listing.html index 80f2f4f2423fb..bce380af785a4 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/web/template/grid/listing.html +++ b/app/code/Magento/AdminNotification/view/adminhtml/web/template/grid/listing.html @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/AdminNotification/view/adminhtml/web/toolbar_entry.js b/app/code/Magento/AdminNotification/view/adminhtml/web/toolbar_entry.js index 2d69f616f029a..ede614f116147 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/web/toolbar_entry.js +++ b/app/code/Magento/AdminNotification/view/adminhtml/web/toolbar_entry.js @@ -1,17 +1,23 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + define([ - "jquery", - "jquery/ui", - "domReady!" + 'jquery', + 'jquery/ui', + 'domReady!' ], function ($) { 'use strict'; - // Mark notification as read via AJAX call + /** + * Mark notification as read via AJAX call. + * + * @param {String} notificationId + */ var markNotificationAsRead = function (notificationId) { var requestUrl = $('.notifications-wrapper .admin__action-dropdown-menu').attr('data-mark-as-read-url'); + $.ajax({ url: requestUrl, type: 'POST', @@ -22,19 +28,24 @@ define([ showLoader: false }); }, - notificationCount = $('.notifications-wrapper').attr('data-notification-count'), - // Remove notification from the list + /** + * Remove notification from the list. + * + * @param {jQuery} notificationEntry + */ removeNotificationFromList = function (notificationEntry) { + var notificationIcon, actionElement; + notificationEntry.remove(); notificationCount--; $('.notifications-wrapper').attr('data-notification-count', notificationCount); - if (notificationCount == 0) { + if (notificationCount == 0) {// eslint-disable-line eqeqeq // Change appearance of the bubble and its behavior when the last notification is removed $('.notifications-wrapper .admin__action-dropdown-menu').remove(); - var notificationIcon = $('.notifications-wrapper .notifications-icon'); + notificationIcon = $('.notifications-wrapper .notifications-icon'); notificationIcon.removeAttr('data-toggle'); notificationIcon.off('click.dropdown'); $('.notifications-action .notifications-counter').text('').hide(); @@ -45,12 +56,16 @@ define([ } $('.notifications-entry-last .notifications-counter').text(notificationCount); // Modify caption of the 'See All' link - var actionElement = $('.notifications-wrapper .admin__action-dropdown-menu .last .action-more'); + actionElement = $('.notifications-wrapper .admin__action-dropdown-menu .last .action-more'); actionElement.text(actionElement.text().replace(/\d+/, notificationCount)); } }, - // Show notification details + /** + * Show notification details. + * + * @param {jQuery} notificationEntry + */ showNotificationDetails = function (notificationEntry) { var notificationDescription = notificationEntry.find('.notifications-entry-description'), notificationDescriptionEnd = notificationEntry.find('.notifications-entry-description-end'); @@ -59,20 +74,22 @@ define([ notificationDescriptionEnd.addClass('_show'); } - if(notificationDescription.hasClass('_cutted')) { + if (notificationDescription.hasClass('_cutted')) { notificationDescription.removeClass('_cutted'); } }; // Show notification description when corresponding item is clicked - $('.notifications-wrapper .admin__action-dropdown-menu .notifications-entry').on('click.showNotification', function (event) { - // hide notification dropdown - $('.notifications-wrapper .notifications-icon').trigger('click.dropdown'); + $('.notifications-wrapper .admin__action-dropdown-menu .notifications-entry').on( + 'click.showNotification', + function (event) { + // hide notification dropdown + $('.notifications-wrapper .notifications-icon').trigger('click.dropdown'); - showNotificationDetails($(this)); - event.stopPropagation(); - - }); + showNotificationDetails($(this)); + event.stopPropagation(); + } + ); // Remove corresponding notification from the list and mark it as read $('.notifications-close').on('click.removeNotification', function (event) { @@ -83,19 +100,19 @@ define([ removeNotificationFromList(notificationEntry); // Checking for last unread notification to hide dropdown - if (notificationCount == 0) { + if (notificationCount == 0) {// eslint-disable-line eqeqeq $('.notifications-wrapper').removeClass('active') - .find('.notifications-action').removeAttr('data-toggle') - .off('click.dropdown'); + .find('.notifications-action') + .removeAttr('data-toggle') + .off('click.dropdown'); } event.stopPropagation(); }); // Hide notifications bubble - if (notificationCount == 0) { + if (notificationCount == 0) {// eslint-disable-line eqeqeq $('.notifications-action .notifications-counter').hide(); } else { $('.notifications-action .notifications-counter').show(); } - -}); \ No newline at end of file +}); diff --git a/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php b/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php index ccbef52b5a818..d35a409b58b98 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php +++ b/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php @@ -1,6 +1,6 @@ '', ImportAdvancedPricing::COL_TIER_PRICE_QTY => '', ImportAdvancedPricing::COL_TIER_PRICE => '', + ImportAdvancedPricing::COL_TIER_PRICE_TYPE => '' ]; /** @@ -279,6 +280,8 @@ protected function getExportData() } /** + * Correct export data. + * * @param array $exportData * @return array * @SuppressWarnings(PHPMD.UnusedLocalVariable) @@ -302,6 +305,12 @@ protected function correctExportData($exportData) : null ); unset($exportRow[ImportAdvancedPricing::VALUE_ALL_GROUPS]); + } elseif ($keyTemplate === ImportAdvancedPricing::COL_TIER_PRICE) { + $exportRow[$keyTemplate] = $row[ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE] + ? $row[ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE] + : $row[ImportAdvancedPricing::COL_TIER_PRICE]; + $exportRow[ImportAdvancedPricing::COL_TIER_PRICE_TYPE] + = $this->tierPriceTypeValue($row[ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE]); } else { $exportRow[$keyTemplate] = $row[$keyTemplate]; } @@ -311,11 +320,25 @@ protected function correctExportData($exportData) $customExportData[$key] = $exportRow; unset($exportRow); } + return $customExportData; } /** - * Get Tier and Group Pricing + * Check type for tier price. + * + * @param string $tierPricePercentage + * @return string + */ + private function tierPriceTypeValue($tierPricePercentage) + { + return $tierPricePercentage + ? ImportAdvancedPricing::TIER_PRICE_TYPE_PERCENT + : ImportAdvancedPricing::TIER_PRICE_TYPE_FIXED; + } + + /** + * Get tier prices. * * @param array $listSku * @param string $table @@ -336,6 +359,7 @@ protected function getTierPrices(array $listSku, $table) ImportAdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'ap.customer_group_id', ImportAdvancedPricing::COL_TIER_PRICE_QTY => 'ap.qty', ImportAdvancedPricing::COL_TIER_PRICE => 'ap.value', + ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE => 'ap.percentage_value', ]; if (isset($exportFilter) && !empty($exportFilter)) { $price = $exportFilter['tier_price']; @@ -371,6 +395,9 @@ protected function getTierPrices(array $listSku, $table) if (isset($price[1]) && !empty($price[1])) { $select->where('ap.value <= ?', $price[1]); } + if (isset($price[0]) && !empty($price[0]) || isset($price[1]) && !empty($price[1])) { + $select->orWhere('ap.percentage_value IS NOT NULL'); + } if (isset($updatedAtFrom) && !empty($updatedAtFrom)) { $select->where('cpe.updated_at >= ?', $updatedAtFrom); } diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php index d751fdc75882d..0bb44c238c720 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php @@ -1,6 +1,6 @@ 'Tier Price data price or quantity value is invalid', ValidatorInterface::ERROR_INVALID_TIER_PRICE_SITE => 'Tier Price data website is invalid', ValidatorInterface::ERROR_INVALID_TIER_PRICE_GROUP => 'Tier Price customer group is invalid', + ValidatorInterface::ERROR_INVALID_TIER_PRICE_TYPE => 'Value for \'tier_price_value_type\' ' . + 'attribute contains incorrect value, acceptable values are Fixed, Discount', ValidatorInterface::ERROR_TIER_DATA_INCOMPLETE => 'Tier Price data is incomplete', ValidatorInterface::ERROR_INVALID_ATTRIBUTE_DECIMAL => 'Value for \'%s\' attribute contains incorrect value, acceptable values are in decimal format', @@ -70,7 +80,7 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract protected $needColumnCheck = true; /** - * Valid column names + * Valid column names. * * @array */ @@ -80,6 +90,7 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract self::COL_TIER_PRICE_CUSTOMER_GROUP, self::COL_TIER_PRICE_QTY, self::COL_TIER_PRICE, + self::COL_TIER_PRICE_TYPE ]; /** @@ -379,7 +390,10 @@ protected function saveAndReplaceAdvancedPrices() $rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] ), 'qty' => $rowData[self::COL_TIER_PRICE_QTY], - 'value' => $rowData[self::COL_TIER_PRICE], + 'value' => $rowData[self::COL_TIER_PRICE_TYPE] === self::TIER_PRICE_TYPE_FIXED + ? $rowData[self::COL_TIER_PRICE] : 0, + 'percentage_value' => $rowData[self::COL_TIER_PRICE_TYPE] === self::TIER_PRICE_TYPE_PERCENT + ? $rowData[self::COL_TIER_PRICE] : null, 'website_id' => $this->getWebsiteId($rowData[self::COL_TIER_PRICE_WEBSITE]) ]; } @@ -429,7 +443,7 @@ protected function saveProductPrices(array $priceData, $table) } } if ($priceIn) { - $this->_connection->insertOnDuplicate($tableName, $priceIn, ['value']); + $this->_connection->insertOnDuplicate($tableName, $priceIn, ['value', 'percentage_value']); } } return $this; @@ -542,19 +556,24 @@ protected function retrieveOldSkus() */ protected function processCountExistingPrices($prices, $table) { + $oldSkus = $this->retrieveOldSkus(); + $existProductIds = array_intersect_key($oldSkus, $prices); + if (!count($existProductIds)) { + return $this; + } + $tableName = $this->_resourceFactory->create()->getTable($table); $productEntityLinkField = $this->getProductEntityLinkField(); $existingPrices = $this->_connection->fetchAssoc( $this->_connection->select()->from( $tableName, ['value_id', $productEntityLinkField, 'all_groups', 'customer_group_id'] - ) + )->where($productEntityLinkField . ' IN (?)', $existProductIds) ); - $oldSkus = $this->retrieveOldSkus(); foreach ($existingPrices as $existingPrice) { - foreach ($oldSkus as $sku => $productId) { - if ($existingPrice[$productEntityLinkField] == $productId && isset($prices[$sku])) { - $this->incrementCounterUpdated($prices[$sku], $existingPrice); + foreach ($prices as $sku => $skuPrices) { + if (isset($oldSkus[$sku]) && $existingPrice[$productEntityLinkField] == $oldSkus[$sku]) { + $this->incrementCounterUpdated($skuPrices, $existingPrice); } } } diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator.php index 051628e7e4a92..ffc191ac655f7 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator.php @@ -1,6 +1,6 @@ hasEmptyColumns($value) ) { $this->_addMessages([self::ERROR_TIER_DATA_INCOMPLETE]); diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPriceType.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPriceType.php new file mode 100644 index 0000000000000..d00d36b0eda27 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPriceType.php @@ -0,0 +1,48 @@ +_addMessages([RowValidatorInterface::ERROR_INVALID_TIER_PRICE_TYPE]); + $isValid = false; + } + + return $isValid; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/Website.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/Website.php index 150555709b8b7..bc57b8482963a 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/Website.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/Website.php @@ -1,6 +1,6 @@ tierPriceType = $objectManager->getObject( + AdvancedPricing\Validator\TierPriceType::class, + [] + ); + } + + /** + * Test for isValid() method. + * + * @dataProvider isValidDataProvider + * @param array $value + * @param bool $expectedResult + */ + public function testIsValid(array $value, $expectedResult) + { + $result = $this->tierPriceType->isValid($value); + $this->assertEquals($expectedResult, $result); + } + + /** + * Data Provider for testIsValid(). + * + * @return array + */ + public function isValidDataProvider() + { + return [ + [ + [AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED], + true + ], + [ + [AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_PERCENT], + true + ], + [ + [], + true + ], + [ + [AdvancedPricing::COL_TIER_PRICE_TYPE => null], + true + ], + [ + [AdvancedPricing::COL_TIER_PRICE_TYPE => 'wrong type'], + false + ] + ]; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php index ad67ee5cc556a..6a0fd28ac7466 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php @@ -1,6 +1,6 @@ advancedPricing + $skuProduct = 'product1'; + $sku = $data[0][AdvancedPricing::COL_SKU]; + $advancedPricing = $this->getAdvancedPricingMock( + [ + 'retrieveOldSkus', + 'validateRow', + 'addRowError', + 'getCustomerGroupId', + 'getWebSiteId', + 'deleteProductTierPrices', + 'getBehavior', + 'saveAndReplaceAdvancedPrices', + 'processCountExistingPrices', + 'processCountNewPrices' + ] + ); + $advancedPricing ->expects($this->any()) ->method('getBehavior') ->willReturn(\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND); $this->dataSourceModel->expects($this->at(0))->method('getNextBunch')->willReturn($data); - $this->advancedPricing->expects($this->any())->method('validateRow')->willReturn(true); + $advancedPricing->expects($this->any())->method('validateRow')->willReturn(true); - $this->advancedPricing->expects($this->any())->method('getCustomerGroupId')->willReturnMap( + $advancedPricing->expects($this->any())->method('getCustomerGroupId')->willReturnMap( [ [$data[0][AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP], $tierCustomerGroupId], ] ); - $this->advancedPricing->expects($this->any())->method('getWebSiteId')->willReturnMap( + $advancedPricing->expects($this->any())->method('getWebSiteId')->willReturnMap( [ [$data[0][AdvancedPricing::COL_TIER_PRICE_WEBSITE], $tierWebsiteId], ] ); - $this->advancedPricing->expects($this->any())->method('saveProductPrices')->will($this->returnSelf()); + $oldSkus = [$sku => $skuProduct]; + $expectedTierPrices[$sku][0][self::LINK_FIELD] = $skuProduct; + $advancedPricing->expects($this->once())->method('retrieveOldSkus')->willReturn($oldSkus); + $this->connection->expects($this->once()) + ->method('insertOnDuplicate') + ->with(self::TABLE_NAME, $expectedTierPrices[$sku], ['value', 'percentage_value']); - $this->advancedPricing->expects($this->any())->method('processCountExistingPrices')->willReturnSelf(); - $this->advancedPricing->expects($this->any())->method('processCountNewPrices')->willReturnSelf(); + $advancedPricing->expects($this->any())->method('processCountExistingPrices')->willReturnSelf(); + $advancedPricing->expects($this->any())->method('processCountNewPrices')->willReturnSelf(); - $result = $this->invokeMethod($this->advancedPricing, 'saveAndReplaceAdvancedPrices'); + $result = $this->invokeMethod($advancedPricing, 'saveAndReplaceAdvancedPrices'); - $this->assertEquals($this->advancedPricing, $result); + $this->assertEquals($advancedPricing, $result); + } + + /** + * Test method saveAndReplaceAdvancedPrices with append import behaviour. + */ + public function testSaveAndReplaceAdvancedPricesAppendBehaviourDataAndCallsWithoutTierPrice() + { + $data = [ + 0 => [ + AdvancedPricing::COL_SKU => 'sku value', + AdvancedPricing::COL_TIER_PRICE_WEBSITE => null, + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups', + AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', + AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED + ], + ]; + $tierCustomerGroupId = 'tier customer group id value'; + $tierWebsiteId = 'tier website id value'; + $expectedTierPrices = []; + + $skuProduct = 'product1'; + $sku = $data[0][AdvancedPricing::COL_SKU]; + $advancedPricing = $this->getAdvancedPricingMock( + [ + 'retrieveOldSkus', + 'validateRow', + 'addRowError', + 'getCustomerGroupId', + 'getWebSiteId', + 'deleteProductTierPrices', + 'getBehavior', + 'saveAndReplaceAdvancedPrices', + 'processCountExistingPrices', + 'processCountNewPrices' + ] + ); + $advancedPricing + ->expects($this->any()) + ->method('getBehavior') + ->willReturn(\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND); + $this->dataSourceModel->expects($this->at(0))->method('getNextBunch')->willReturn($data); + $advancedPricing->expects($this->any())->method('validateRow')->willReturn(true); + + $advancedPricing->expects($this->any())->method('getCustomerGroupId')->willReturnMap( + [ + [$data[0][AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP], $tierCustomerGroupId], + ] + ); + + $advancedPricing->expects($this->any())->method('getWebSiteId')->willReturnMap( + [ + [$data[0][AdvancedPricing::COL_TIER_PRICE_WEBSITE], $tierWebsiteId], + ] + ); + + $oldSkus = [$sku => $skuProduct]; + $expectedTierPrices[$sku][0][self::LINK_FIELD] = $skuProduct; + $advancedPricing->expects($this->never())->method('retrieveOldSkus')->willReturn($oldSkus); + $this->connection->expects($this->never()) + ->method('insertOnDuplicate') + ->with(self::TABLE_NAME, $expectedTierPrices[$sku], ['value', 'percentage_value']); + + $advancedPricing->expects($this->any())->method('processCountExistingPrices')->willReturnSelf(); + $advancedPricing->expects($this->any())->method('processCountNewPrices')->willReturnSelf(); + + $result = $this->invokeMethod($advancedPricing, 'saveAndReplaceAdvancedPrices'); + + $this->assertEquals($advancedPricing, $result); } /** @@ -575,6 +665,7 @@ public function saveAndReplaceAdvancedPricesAppendBehaviourDataProvider() AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups ', AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED ], ], '$tierCustomerGroupId' => 'tier customer group id value', @@ -585,25 +676,25 @@ public function saveAndReplaceAdvancedPricesAppendBehaviourDataProvider() 'sku value' => [ [ 'all_groups' => false, - //$rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS 'customer_group_id' => 'tier customer group id value', - //$tierCustomerGroupId 'qty' => 'tier price qty value', 'value' => 'tier price value', 'website_id' => 'tier website id value', + 'percentage_value' => null ], ], ], ], - [// tier customer group is equal to all group + [ '$data' => [ 0 => [ AdvancedPricing::COL_SKU => 'sku value', //tier AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier price website value', - AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => AdvancedPricing::VALUE_ALL_GROUPS, + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups ', AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_PERCENT ], ], '$tierCustomerGroupId' => 'tier customer group id value', @@ -613,33 +704,44 @@ public function saveAndReplaceAdvancedPricesAppendBehaviourDataProvider() '$expectedTierPrices' => [ 'sku value' => [ [ - 'all_groups' => true, - //$rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS + 'all_groups' => false, 'customer_group_id' => 'tier customer group id value', - //$tierCustomerGroupId 'qty' => 'tier price qty value', - 'value' => 'tier price value', + 'value' => 0, + 'percentage_value' => 'tier price value', 'website_id' => 'tier website id value', ], ], ], ], - [ + [// tier customer group is equal to all group '$data' => [ 0 => [ AdvancedPricing::COL_SKU => 'sku value', //tier - AdvancedPricing::COL_TIER_PRICE_WEBSITE => null, - AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups', + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier price website value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => AdvancedPricing::VALUE_ALL_GROUPS, AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED ], ], '$tierCustomerGroupId' => 'tier customer group id value', '$groupCustomerGroupId' => 'group customer group id value', '$tierWebsiteId' => 'tier website id value', '$groupWebsiteId' => 'group website id value', - '$expectedTierPrices' => [], + '$expectedTierPrices' => [ + 'sku value' => [ + [ + 'all_groups' => true, + 'customer_group_id' => 'tier customer group id value', + 'qty' => 'tier price qty value', + 'value' => 'tier price value', + 'website_id' => 'tier website id value', + 'percentage_value' => null + ], + ], + ], ], [ '$data' => [ @@ -650,6 +752,7 @@ public function saveAndReplaceAdvancedPricesAppendBehaviourDataProvider() AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups', AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED ], ], '$tierCustomerGroupId' => 'tier customer group id value', @@ -660,12 +763,11 @@ public function saveAndReplaceAdvancedPricesAppendBehaviourDataProvider() 'sku value' => [ [ 'all_groups' => false, - //$rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS 'customer_group_id' => 'tier customer group id value', - //$tierCustomerGroupId 'qty' => 'tier price qty value', 'value' => 'tier price value', 'website_id' => 'tier website id value', + 'percentage_value' => null ], ] ], @@ -746,7 +848,7 @@ public function testSaveProductPrices($priceData, $oldSkus, $priceIn, $callNum) $this->connection->expects($this->exactly($callNum)) ->method('insertOnDuplicate') - ->with(self::TABLE_NAME, $priceIn, ['value']); + ->with(self::TABLE_NAME, $priceIn, ['value', 'percentage_value']); $this->invokeMethod($this->advancedPricing, 'saveProductPrices', [$priceData, 'table']); } diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Indexer/Product/Price/Plugin/ImportTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Indexer/Product/Price/Plugin/ImportTest.php index d65271d9ace54..67f6d33a9df15 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Indexer/Product/Price/Plugin/ImportTest.php +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Indexer/Product/Price/Plugin/ImportTest.php @@ -1,6 +1,6 @@ @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/di.xml b/app/code/Magento/AdvancedPricingImportExport/etc/di.xml index c3444069c14c0..950054ab52412 100644 --- a/app/code/Magento/AdvancedPricingImportExport/etc/di.xml +++ b/app/code/Magento/AdvancedPricingImportExport/etc/di.xml @@ -1,7 +1,7 @@ @@ -14,6 +14,7 @@ Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\TierPrice Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\Website + Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\TierPriceType diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/export.xml b/app/code/Magento/AdvancedPricingImportExport/etc/export.xml index f04e399fdfedb..da176e7bd73e1 100644 --- a/app/code/Magento/AdvancedPricingImportExport/etc/export.xml +++ b/app/code/Magento/AdvancedPricingImportExport/etc/export.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/import.xml b/app/code/Magento/AdvancedPricingImportExport/etc/import.xml index 711a7bf270ce6..80c8873aad387 100644 --- a/app/code/Magento/AdvancedPricingImportExport/etc/import.xml +++ b/app/code/Magento/AdvancedPricingImportExport/etc/import.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/module.xml b/app/code/Magento/AdvancedPricingImportExport/etc/module.xml index 4ae0d6516e66f..f9ad9f34b2ad6 100644 --- a/app/code/Magento/AdvancedPricingImportExport/etc/module.xml +++ b/app/code/Magento/AdvancedPricingImportExport/etc/module.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/AdvancedPricingImportExport/registration.php b/app/code/Magento/AdvancedPricingImportExport/registration.php index 86dcff3d60a68..a9477f3a9a1fb 100644 --- a/app/code/Magento/AdvancedPricingImportExport/registration.php +++ b/app/code/Magento/AdvancedPricingImportExport/registration.php @@ -1,6 +1,6 @@ subscriptionStatusProvider = $labelStatusProvider; + } + + /** + * Unset some non-related element parameters + * + * @param \Magento\Framework\Data\Form\Element\AbstractElement $element + * @return string + */ + public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element) + { + $element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue(); + $element->setData( + 'value', + $this->prepareLabelValue() + ); + return parent::render($element); + } + + /** + * Prepare label for subscription status + * + * @return string + */ + private function prepareLabelValue() + { + return __('Subscription status') . ': ' . __($this->subscriptionStatusProvider->getStatus()); + } +} diff --git a/app/code/Magento/Analytics/Controller/Adminhtml/BasicTier/SignUp.php b/app/code/Magento/Analytics/Controller/Adminhtml/BasicTier/SignUp.php new file mode 100644 index 0000000000000..73c39d7c14a59 --- /dev/null +++ b/app/code/Magento/Analytics/Controller/Adminhtml/BasicTier/SignUp.php @@ -0,0 +1,62 @@ +config = $config; + parent::__construct($context); + } + + /** + * Check admin permissions for this controller + * + * @return boolean + */ + protected function _isAllowed() + { + return $this->_authorization->isAllowed('Magento_Analytics::report_basic_tier'); + } + + /** + * Provides link to Basic Tier signup + * + * @return \Magento\Framework\Controller\AbstractResult + */ + public function execute() + { + return $this->resultRedirectFactory->create()->setUrl( + $this->config->getConfigDataValue($this->basicTierUrlPath) + ); + } +} diff --git a/app/code/Magento/Analytics/Controller/Adminhtml/Reports/Show.php b/app/code/Magento/Analytics/Controller/Adminhtml/Reports/Show.php new file mode 100644 index 0000000000000..ac9d93a579ffd --- /dev/null +++ b/app/code/Magento/Analytics/Controller/Adminhtml/Reports/Show.php @@ -0,0 +1,71 @@ +reportUrlProvider = $reportUrlProvider; + parent::__construct($context); + } + + /** + * Check admin permissions for this controller. + * + * @return boolean + */ + protected function _isAllowed() + { + return $this->_authorization->isAllowed('Magento_Analytics::analytics_settings'); + } + + /** + * Redirect to resource with reports. + * + * @return Redirect $resultRedirect + */ + public function execute() + { + /** @var Redirect $resultRedirect */ + $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + try { + $resultRedirect->setUrl($this->reportUrlProvider->getUrl()); + } catch (LocalizedException $e) { + $this->getMessageManager()->addExceptionMessage($e, $e->getMessage()); + $resultRedirect->setPath('adminhtml'); + } catch (\Exception $e) { + $this->getMessageManager()->addExceptionMessage( + $e, + __('Sorry, there has been an error processing your request. Please try again later.') + ); + $resultRedirect->setPath('adminhtml'); + } + + return $resultRedirect; + } +} diff --git a/app/code/Magento/Analytics/Controller/Adminhtml/Subscription/Activate.php b/app/code/Magento/Analytics/Controller/Adminhtml/Subscription/Activate.php new file mode 100644 index 0000000000000..56cc44bbc95dd --- /dev/null +++ b/app/code/Magento/Analytics/Controller/Adminhtml/Subscription/Activate.php @@ -0,0 +1,118 @@ +subscription = $subscription; + $this->logger = $logger; + $this->notificationTime = $notificationTime; + parent::__construct($context); + } + + /** + * Check admin permissions for this controller + * + * @return boolean + */ + protected function _isAllowed() + { + return $this->_authorization->isAllowed('Magento_Analytics::analytics_settings'); + } + + /** + * Activate subscription to Magento Analytics via AJAX. + * + * @return Json + */ + public function execute() + { + try { + if ($this->getRequest()->getParam($this->subscriptionApprovedField)) { + $this->subscription->enable(); + } else { + $this->notificationTime->unsetLastTimeNotificationValue(); + } + $responseContent = [ + 'success' => true, + 'error_message' => '', + ]; + } catch (LocalizedException $e) { + $responseContent = [ + 'success' => false, + 'error_message' => $e->getMessage(), + ]; + $this->logger->error($e->getMessage()); + } catch (\Exception $e) { + $responseContent = [ + 'success' => false, + 'error_message' => __( + 'Sorry, there was an error processing your registration request to Magento Analytics. ' + . 'Please try again later.' + ), + ]; + $this->logger->error($e->getMessage()); + } + /** @var Json $resultJson */ + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + return $resultJson->setData($responseContent); + } +} diff --git a/app/code/Magento/Analytics/Controller/Adminhtml/Subscription/Postpone.php b/app/code/Magento/Analytics/Controller/Adminhtml/Subscription/Postpone.php new file mode 100644 index 0000000000000..b87730132e3d4 --- /dev/null +++ b/app/code/Magento/Analytics/Controller/Adminhtml/Subscription/Postpone.php @@ -0,0 +1,101 @@ +dateTimeFactory = $dateTimeFactory; + $this->notificationTime = $notificationTime; + $this->logger = $logger; + parent::__construct($context); + + } + + /** + * Check admin permissions for this controller + * + * @return boolean + */ + protected function _isAllowed() + { + return $this->_authorization->isAllowed('Magento_Analytics::analytics_settings'); + } + + /** + * Postpones notification about subscription + * + * @return Json + */ + public function execute() + { + try { + $dateTime = $this->dateTimeFactory->create(); + $responseContent = [ + 'success' => $this->notificationTime->storeLastTimeNotification($dateTime->getTimestamp()), + 'error_message' => '' + ]; + } catch (LocalizedException $e) { + $this->logger->error($e->getMessage()); + $responseContent = [ + 'success' => false, + 'error_message' => $e->getMessage() + ]; + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + $responseContent = [ + 'success' => false, + 'error_message' => __('Error occurred during postponement notification') + ]; + } + /** @var Json $resultJson */ + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + return $resultJson->setData($responseContent); + } +} diff --git a/app/code/Magento/Analytics/Cron/CollectData.php b/app/code/Magento/Analytics/Cron/CollectData.php new file mode 100644 index 0000000000000..5b2abd2fbe6a9 --- /dev/null +++ b/app/code/Magento/Analytics/Cron/CollectData.php @@ -0,0 +1,53 @@ +exportDataHandler = $exportDataHandler; + $this->subscriptionStatus = $subscriptionStatus; + } + + /** + * @return bool + */ + public function execute() + { + if ($this->subscriptionStatus->getStatus() === SubscriptionStatusProvider::ENABLED) { + $this->exportDataHandler->prepareExportData(); + } + + return true; + } +} diff --git a/app/code/Magento/Analytics/Cron/SignUp.php b/app/code/Magento/Analytics/Cron/SignUp.php new file mode 100644 index 0000000000000..b8d79ae2707b3 --- /dev/null +++ b/app/code/Magento/Analytics/Cron/SignUp.php @@ -0,0 +1,131 @@ +connector = $connector; + $this->configWriter = $configWriter; + $this->inboxFactory = $inboxFactory; + $this->inboxResource = $inboxResource; + $this->flagManager = $flagManager; + $this->reinitableConfig = $reinitableConfig; + } + + /** + * Execute scheduled subscription operation + * In case of failure writes message to notifications inbox + * + * @return bool + */ + public function execute() + { + $attemptsCount = $this->flagManager->getFlagData(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); + if ($attemptsCount === null) { + $this->deleteAnalyticsCronExpr(); + return false; + } + + if ($attemptsCount <= 0) { + $this->deleteAnalyticsCronExpr(); + $this->flagManager->deleteFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); + $inboxNotification = $this->inboxFactory->create(); + $inboxNotification->addNotice( + "Analytics subscription unsuccessful", + "Analytics subscription unsuccessful" + ); + $this->inboxResource->save($inboxNotification); + return false; + } + + $attemptsCount -= 1; + $this->flagManager->saveFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, $attemptsCount); + $signUpResult = $this->connector->execute('signUp'); + if ($signUpResult === false) { + return false; + } + + $this->deleteAnalyticsCronExpr(); + $this->flagManager->deleteFlag(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); + return true; + } + + /** + * Delete cron schedule setting into config. + * + * Delete cron schedule setting for subscription handler into config and + * re-initialize config cache to avoid auto-generate new schedule items. + * + * @return bool + */ + private function deleteAnalyticsCronExpr() + { + $this->configWriter->delete(SubscriptionHandler::CRON_STRING_PATH); + $this->reinitableConfig->reinit(); + return true; + } +} diff --git a/app/code/Magento/Analytics/Cron/Update.php b/app/code/Magento/Analytics/Cron/Update.php new file mode 100644 index 0000000000000..4ed1346ade9a5 --- /dev/null +++ b/app/code/Magento/Analytics/Cron/Update.php @@ -0,0 +1,78 @@ +connector = $connector; + $this->configWriter = $configWriter; + $this->reinitableConfig = $reinitableConfig; + $this->flagManager = $flagManager; + } + + /** + * Execute scheduled update operation + * + * @return bool + */ + public function execute() + { + $updateResult = $this->connector->execute('update'); + if ($updateResult === false) { + return false; + } + $this->configWriter->delete(BaseUrlConfigPlugin::UPDATE_CRON_STRING_PATH); + $this->flagManager->deleteFlag(BaseUrlConfigPlugin::OLD_BASE_URL_FLAG_CODE); + $this->reinitableConfig->reinit(); + return true; + } +} diff --git a/app/code/Magento/Analytics/LICENSE.txt b/app/code/Magento/Analytics/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/Analytics/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/Analytics/LICENSE_AFL.txt b/app/code/Magento/Analytics/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/Analytics/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/Analytics/Model/AnalyticsToken.php b/app/code/Magento/Analytics/Model/AnalyticsToken.php new file mode 100644 index 0000000000000..abef37f3a62d3 --- /dev/null +++ b/app/code/Magento/Analytics/Model/AnalyticsToken.php @@ -0,0 +1,92 @@ +reinitableConfig = $reinitableConfig; + $this->config = $config; + $this->configWriter = $configWriter; + } + + /** + * Get Magento BI token value. + * + * @return string|null + */ + public function getToken() + { + return $this->config->getValue($this->tokenPath); + } + + /** + * Stores Magento BI token value. + * + * @param string $value + * + * @return bool + */ + public function storeToken($value) + { + $this->configWriter->save($this->tokenPath, $value); + $this->reinitableConfig->reinit(); + + return true; + } + + /** + * Check Magento BI token value exist. + * + * @return bool + */ + public function isTokenExist() + { + return (bool)$this->getToken(); + } +} diff --git a/app/code/Magento/Analytics/Model/Condition/CanViewNotification.php b/app/code/Magento/Analytics/Model/Condition/CanViewNotification.php new file mode 100644 index 0000000000000..a7dc93bcab800 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Condition/CanViewNotification.php @@ -0,0 +1,68 @@ +notificationTime = $notificationTime; + $this->dateTimeFactory = $dateTimeFactory; + } + + /** + * Validate is notification popup can be shown + * + * @return bool + */ + public function validate() + { + $lastNotificationTime = $this->notificationTime->getLastTimeNotification(); + if (!$lastNotificationTime) { + return false; + } + $datetime = $this->dateTimeFactory->create(); + return ( + $datetime->getTimestamp() >= $lastNotificationTime + $this->notificationInterval + ); + } +} diff --git a/app/code/Magento/Analytics/Model/Config.php b/app/code/Magento/Analytics/Model/Config.php new file mode 100644 index 0000000000000..a4601ec4430c4 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config.php @@ -0,0 +1,40 @@ +data = $data; + } + + /** + * Get config value by key. + * + * @param string|null $key + * @param string|null $default + * @return array + */ + public function get($key = null, $default = null) + { + return $this->data->get($key, $default); + } +} diff --git a/app/code/Magento/Analytics/Model/Config/Backend/CollectionTime.php b/app/code/Magento/Analytics/Model/Config/Backend/CollectionTime.php new file mode 100644 index 0000000000000..208f3a5dad7c8 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config/Backend/CollectionTime.php @@ -0,0 +1,91 @@ +configWriter = $configWriter; + parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data); + } + + /** + * {@inheritdoc} + * + * {@inheritdoc}. Set schedule setting for cron. + * + * @return Value + */ + public function afterSave() + { + $result = preg_match('#(?\d{2}),(?\d{2}),(?\d{2})#', $this->getValue(), $time); + + if (!$result) { + throw new LocalizedException(__('Time value has an unsupported format')); + } + + $cronExprArray = [ + $time['min'], # Minute + $time['hour'], # Hour + '*', # Day of the Month + '*', # Month of the Year + '*', # Day of the Week + ]; + + $cronExprString = join(' ', $cronExprArray); + + try { + $this->configWriter->save(self::CRON_SCHEDULE_PATH, $cronExprString); + } catch (\Exception $e) { + $this->_logger->error($e->getMessage()); + throw new LocalizedException(__('Cron settings can\'t be saved')); + } + + return parent::afterSave(); + } +} diff --git a/app/code/Magento/Analytics/Model/Config/Backend/Enabled.php b/app/code/Magento/Analytics/Model/Config/Backend/Enabled.php new file mode 100644 index 0000000000000..8cbb64fb26337 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config/Backend/Enabled.php @@ -0,0 +1,71 @@ +subscriptionHandler = $subscriptionHandler; + parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data); + } + + /** + * Add additional handling after config value was saved. + * + * @return Value + * @throws LocalizedException + */ + public function afterSave() + { + try { + $this->subscriptionHandler->process($this); + } catch (\Exception $e) { + $this->_logger->error($e->getMessage()); + throw new LocalizedException(__('There was an error save new configuration value.')); + } + + return parent::afterSave(); + } +} diff --git a/app/code/Magento/Analytics/Model/Config/Backend/Enabled/SubscriptionHandler.php b/app/code/Magento/Analytics/Model/Config/Backend/Enabled/SubscriptionHandler.php new file mode 100644 index 0000000000000..0f169a780dcb6 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config/Backend/Enabled/SubscriptionHandler.php @@ -0,0 +1,174 @@ +configWriter = $configWriter; + $this->flagManager = $flagManager; + $this->analyticsToken = $analyticsToken; + $this->notificationTime = $notificationTime; + } + + /** + * Performs change subscription environment on config value change. + * + * Activate process of subscription handling + * if subscription was activated and Analytics token has not been received + * or interrupt subscription handling. + * + * @param Value $configValue + * + * @return bool + */ + public function process(Value $configValue) + { + if (!$configValue->isValueChanged()) { + return true; + } + + $enabled = $configValue->getData('value'); + + if (!$enabled) { + $this->disableCollectionData(); + } + + if (!$this->analyticsToken->isTokenExist()) { + if ($enabled) { + $this->setCronSchedule(); + $this->setAttemptsFlag(); + $this->notificationTime->unsetLastTimeNotificationValue(); + } else { + $this->unsetAttemptsFlag(); + } + } + + return true; + } + + /** + * Set cron schedule setting into config for activation of subscription process. + * + * @return bool + */ + private function setCronSchedule() + { + $cronExprArray = [ + '0', # Minute + '*', # Hour + '*', # Day of the Month + '*', # Month of the Year + '*', # Day of the Week + ]; + + $cronExprString = join(' ', $cronExprArray); + + $this->configWriter->save(self::CRON_STRING_PATH, $cronExprString); + + return true; + } + + /** + * Set flag as reserve counter of attempts subscription operation. + * + * @return bool + */ + private function setAttemptsFlag() + { + return $this->flagManager + ->saveFlag(self::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, $this->attemptsInitValue); + } + + /** + * Unset flag of attempts subscription operation. + * + * @return bool + */ + private function unsetAttemptsFlag() + { + return $this->flagManager + ->deleteFlag(self::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); + } + + /** + * Unset schedule of collection data cron. + * + * @return bool + */ + private function disableCollectionData() + { + $this->configWriter->delete(CollectionTime::CRON_SCHEDULE_PATH); + + return true; + } +} diff --git a/app/code/Magento/Analytics/Model/Config/Backend/Vertical.php b/app/code/Magento/Analytics/Model/Config/Backend/Vertical.php new file mode 100644 index 0000000000000..c9759855e843f --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config/Backend/Vertical.php @@ -0,0 +1,32 @@ +getValue())) { + throw new LocalizedException(__('Please select a vertical.')); + } + + return $this; + } +} diff --git a/app/code/Magento/Analytics/Model/Config/Data.php b/app/code/Magento/Analytics/Model/Config/Data.php new file mode 100644 index 0000000000000..addc305b71a9e --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config/Data.php @@ -0,0 +1,29 @@ + [ + * 'name' => 'file_name', + * 'providers' => [ + * 'reportProvider' => [ + * 'name' => 'report_provider_name', + * 'class' => 'Magento\Analytics\ReportXml\ReportProvider', + * 'parameters' =>[ + * 'name' => 'report_name', + * ], + * ], + * 'customProvider' => [ + * 'name' => 'custom_provider_name', + * 'class' => 'Magento\Analytics\Model\CustomProvider', + * ], + * ], + * ] + * ]; + */ + public function execute($configData) + { + if (!isset($configData['config'][0]['file'])) { + return []; + } + + $files = []; + foreach ($configData['config'][0]['file'] as $fileData) { + /** just one set of providers is allowed by xsd */ + $providers = reset($fileData['providers']); + foreach ($providers as $providerType => $providerDataSet) { + /** just one set of provider data is allowed by xsd */ + $providerData = reset($providerDataSet); + /** just one set of parameters is allowed by xsd */ + $providerData['parameters'] = !empty($providerData['parameters']) + ? reset($providerData['parameters']) + : []; + $providerData['parameters'] = array_map( + 'reset', + $providerData['parameters'] + ); + $providers[$providerType] = $providerData; + } + $files[$fileData['name']] = $fileData; + $files[$fileData['name']]['providers'] = $providers; + } + return $files; + } +} diff --git a/app/code/Magento/Analytics/Model/Config/Reader.php b/app/code/Magento/Analytics/Model/Config/Reader.php new file mode 100644 index 0000000000000..e76cc57ee37d8 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config/Reader.php @@ -0,0 +1,52 @@ +mapper = $mapper; + $this->readers = $readers; + } + + /** + * Read configuration scope. + * + * @param string|null $scope + * @return array + */ + public function read($scope = null) + { + $data = []; + foreach ($this->readers as $reader) { + $data = array_merge_recursive($data, $reader->read($scope)); + } + + return $this->mapper->execute($data); + } +} diff --git a/app/code/Magento/Analytics/Model/Config/Reader/Xml.php b/app/code/Magento/Analytics/Model/Config/Reader/Xml.php new file mode 100644 index 0000000000000..4a333f00324f9 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config/Reader/Xml.php @@ -0,0 +1,61 @@ + 'name', + ]; + + /** + * @param FileResolverInterface $fileResolver + * @param ConverterInterface $converter + * @param SchemaLocatorInterface $schemaLocator + * @param ValidationStateInterface $validationState + * @param string $fileName + * @param array $idAttributes + * @param string $domDocumentClass + * @param string $defaultScope + */ + public function __construct( + FileResolverInterface $fileResolver, + ConverterInterface $converter, + SchemaLocatorInterface $schemaLocator, + ValidationStateInterface $validationState, + $fileName = 'analytics.xml', + $idAttributes = [], + $domDocumentClass = Dom::class, + $defaultScope = 'global' + ) { + parent::__construct( + $fileResolver, + $converter, + $schemaLocator, + $validationState, + $fileName, + $idAttributes, + $domDocumentClass, + $defaultScope + ); + } +} diff --git a/app/code/Magento/Analytics/Model/Config/SchemaLocator.php b/app/code/Magento/Analytics/Model/Config/SchemaLocator.php new file mode 100644 index 0000000000000..1785174f4da65 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config/SchemaLocator.php @@ -0,0 +1,56 @@ +urnResolver = $urnResolver; + } + + /** + * Get path to merged config schema. + * + * @return string|null + */ + public function getSchema() + { + return $this->urnResolver->getRealPath($this->schema); + } + + /** + * Get path to pre file validation schema. + * + * @return string|null + */ + public function getPerFileSchema() + { + return null; + } +} diff --git a/app/code/Magento/Analytics/Model/Config/Source/Vertical.php b/app/code/Magento/Analytics/Model/Config/Source/Vertical.php new file mode 100644 index 0000000000000..6bc00465b0607 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Config/Source/Vertical.php @@ -0,0 +1,51 @@ +verticals = $verticals; + } + + /** + * {@inheritdoc} + */ + public function toOptionArray() + { + $result = [ + ['value' => '', 'label' => __('--Please Select--')] + ]; + + foreach ($this->verticals as $vertical) { + $result[] = ['value' => $vertical, 'label' => __($vertical)]; + } + + return $result; + } +} diff --git a/app/code/Magento/Analytics/Model/ConfigInterface.php b/app/code/Magento/Analytics/Model/ConfigInterface.php new file mode 100644 index 0000000000000..be719e5640f1a --- /dev/null +++ b/app/code/Magento/Analytics/Model/ConfigInterface.php @@ -0,0 +1,22 @@ + 'command_class_name'. + * + * The list may be configured in each module via '/etc/di.xml'. + * + * @var string[] + */ + private $commands; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @param array $commands + * @param ObjectManagerInterface $objectManager + */ + public function __construct( + array $commands, + ObjectManagerInterface $objectManager + ) { + $this->commands = $commands; + $this->objectManager = $objectManager; + } + + /** + * Executes a command in accordance with the given name. + * + * @param string $commandName + * @return bool + * @throws NotFoundException if the command is not found. + */ + public function execute($commandName) + { + if (!array_key_exists($commandName, $this->commands)) { + throw new NotFoundException(__('Command was not found.')); + } + + /** @var \Magento\Analytics\Model\Connector\CommandInterface $command */ + $command = $this->objectManager->create($this->commands[$commandName]); + + return $command->execute(); + } +} diff --git a/app/code/Magento/Analytics/Model/Connector/CommandInterface.php b/app/code/Magento/Analytics/Model/Connector/CommandInterface.php new file mode 100644 index 0000000000000..8ac1a5fce4371 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Connector/CommandInterface.php @@ -0,0 +1,20 @@ +curlFactory = $curlFactory; + $this->responseFactory = $responseFactory; + $this->logger = $logger; + } + + /** + * {@inheritdoc} + */ + public function request($method, $url, $body = '', array $headers = [], $version = '1.1') + { + $curl = $this->curlFactory->create(); + + $curl->write($method, $url, $version, $headers, $body); + + $result = $curl->read(); + + if ($curl->getErrno()) { + $this->logger->critical( + new \Exception( + sprintf( + 'MBI service CURL connection error #%s: %s', + $curl->getErrno(), + $curl->getError() + ) + ) + ); + + return false; + } + + return $this->responseFactory->create($result); + } +} diff --git a/app/code/Magento/Analytics/Model/Connector/Http/ClientInterface.php b/app/code/Magento/Analytics/Model/Connector/Http/ClientInterface.php new file mode 100644 index 0000000000000..37eb6865544a0 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Connector/Http/ClientInterface.php @@ -0,0 +1,29 @@ +analyticsToken = $analyticsToken; + $this->httpClient = $httpClient; + $this->config = $config; + $this->logger = $logger; + } + + /** + * Performs obtaining of an OTP from the MBI service. + * + * Returns received OTP or FALSE in case of failure. + * + * @return string|false + */ + public function call() + { + $otp = false; + + if ($this->analyticsToken->isTokenExist()) { + try { + $response = $this->httpClient->request( + ZendClient::POST, + $this->config->getValue($this->otpUrlConfigPath), + $this->getRequestJson(), + ['Content-Type: application/json'] + ); + + if ($response) { + $otp = $this->extractOtp($response); + + if (!$otp) { + $this->logger->warning( + sprintf( + 'Obtaining of an OTP from the MBI service has been failed: %s', + !empty($response->getBody()) ? $response->getBody() : 'Response body is empty.' + ) + ); + } + } + } catch (\Exception $e) { + $this->logger->critical($e); + } + } + + return $otp; + } + + /** + * Prepares request data in JSON format. + * + * @return string + */ + private function getRequestJson() + { + return json_encode( + [ + "access-token" => $this->analyticsToken->getToken(), + "url" => $this->config->getValue(Store::XML_PATH_SECURE_BASE_URL), + ] + ); + } + + /** + * Extracts an OTP from the response. + * + * Returns the OTP or FALSE if the OTP is not found. + * + * @param HttpResponse $response + * @return string|false + */ + private function extractOtp(HttpResponse $response) + { + $otp = false; + + if ($response->getStatus() === 201) { + $body = json_decode($response->getBody(), 1); + + $otp = !empty($body['otp']) ? $body['otp'] : false; + } + + return $otp; + } +} diff --git a/app/code/Magento/Analytics/Model/Connector/SignUpCommand.php b/app/code/Magento/Analytics/Model/Connector/SignUpCommand.php new file mode 100644 index 0000000000000..f9fd78420be46 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Connector/SignUpCommand.php @@ -0,0 +1,74 @@ +analyticsToken = $analyticsToken; + $this->integrationManager = $integrationManager; + $this->signUpRequest = $signUpRequest; + } + + /** + * Executes signUp command + * + * During this call Magento generates or retrieves access token for the integration user + * In case successful generation Magento activates user and sends access token to MA + * As the response, Magento receives a token to MA + * Magento stores this token in System Configuration + * + * This method returns true in case of success + * + * @return bool + */ + public function execute() + { + $integrationToken = $this->integrationManager->generateToken(); + if ($integrationToken) { + $this->integrationManager->activateIntegration(); + $responseToken = $this->signUpRequest->call($integrationToken->getToken()); + if ($responseToken) { + $this->analyticsToken->storeToken($responseToken); + } + } + return ((bool)$integrationToken && isset($responseToken) && (bool)$responseToken); + } +} diff --git a/app/code/Magento/Analytics/Model/Connector/SignUpRequest.php b/app/code/Magento/Analytics/Model/Connector/SignUpRequest.php new file mode 100644 index 0000000000000..74bfa84d0ef62 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Connector/SignUpRequest.php @@ -0,0 +1,135 @@ +config = $config; + $this->httpClient = $httpClient; + $this->logger = $logger; + } + + /** + * Prepares request data in JSON format. + * + * @param string $integrationToken + * @return string + */ + private function getRequestJson($integrationToken) + { + return json_encode( + [ + "token" => $integrationToken, + "url" => $this->config->getConfigDataValue( + Store::XML_PATH_SECURE_BASE_URL + ) + ] + ); + } + + /** + * Extracts an MBI access token from the response. + * + * Returns the token or FALSE if the token is not found. + * + * @param HttpResponse $response + * @return string|false + */ + private function extractAccessToken(HttpResponse $response) + { + $token = false; + + if ($response->getStatus() === 201) { + $body = json_decode($response->getBody(), 1); + + if (isset($body['access-token']) && !empty($body['access-token'])) { + $token = $body['access-token']; + } + } + + return $token; + } + + /** + * Performs a 'signUp' call to MBI service. + * + * Returns MBI access token or FALSE in case of failure. + * + * @param string $integrationToken + * @return string|false + */ + public function call($integrationToken) + { + $token = false; + + try { + $response = $this->httpClient->request( + ZendClient::POST, + $this->config->getConfigDataValue($this->signUpUrlPath), + $this->getRequestJson($integrationToken), + ['Content-Type: application/json'] + ); + + if ($response) { + $token = $this->extractAccessToken($response); + + if (!$token) { + $this->logger->warning( + sprintf( + 'Subscription for MBI service has been failed. An error occurred during token exchange: %s', + !empty($response->getBody()) ? $response->getBody() : 'Response body is empty.' + ) + ); + } + } + } catch (\Exception $e) { + $this->logger->critical($e); + } + + return $token; + } +} diff --git a/app/code/Magento/Analytics/Model/Connector/UpdateCommand.php b/app/code/Magento/Analytics/Model/Connector/UpdateCommand.php new file mode 100644 index 0000000000000..ba9e779122f55 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Connector/UpdateCommand.php @@ -0,0 +1,124 @@ +analyticsToken = $analyticsToken; + $this->httpClient = $httpClient; + $this->config = $config; + $this->logger = $logger; + $this->flagManager = $flagManager; + } + + /** + * Executes update request to MBI api in case store url was changed + * @return bool + */ + public function execute() + { + $result = false; + try { + if ($this->analyticsToken->isTokenExist()) { + $response = $this->httpClient->request( + ZendClient::PUT, + $this->config->getConfigDataValue($this->updateUrlPath), + $this->getRequestJson(), + ['Content-Type: application/json'] + ); + + if ($response) { + $result = $response->getStatus() === 201; + if (!$result) { + $this->logger->warning( + sprintf( + 'Update of the subscription for MBI service has been failed: %s', + !empty($response->getBody()) ? $response->getBody() : 'Response body is empty.' + ) + ); + } + } + } + } catch (\Exception $e) { + $this->logger->critical($e); + } + + return $result; + } + + /** + * Prepares request data in JSON format. + * @return string + */ + private function getRequestJson() + { + return json_encode( + [ + "url" => $this->flagManager->getFlagData(BaseUrlConfigPlugin::OLD_BASE_URL_FLAG_CODE), + "new-url" => $this->config->getConfigDataValue( + Store::XML_PATH_SECURE_BASE_URL + ), + "access-token" => $this->analyticsToken->getToken(), + ] + ); + } +} diff --git a/app/code/Magento/Analytics/Model/Cryptographer.php b/app/code/Magento/Analytics/Model/Cryptographer.php new file mode 100644 index 0000000000000..b3b691399994d --- /dev/null +++ b/app/code/Magento/Analytics/Model/Cryptographer.php @@ -0,0 +1,130 @@ +analyticsToken = $analyticsToken; + $this->encodedContextFactory = $encodedContextFactory; + } + + /** + * Encrypt input data. + * + * @param string $source + * @return EncodedContext + * @throws LocalizedException + */ + public function encode($source) + { + if (!is_string($source)) { + try { + $source = (string)$source; + } catch (\Exception $e) { + throw new LocalizedException(__('Input data must be string or convertible into string.')); + } + } elseif (!$source) { + throw new LocalizedException(__('Input data must be non-empty string.')); + } + if (!$this->validateCipherMethod($this->cipherMethod)) { + throw new LocalizedException(__('Not valid cipher method.')); + } + $initializationVector = $this->getInitializationVector(); + + $encodedContext = $this->encodedContextFactory->create([ + 'content' => openssl_encrypt( + $source, + $this->cipherMethod, + $this->getKey(), + OPENSSL_RAW_DATA, + $initializationVector + ), + 'initializationVector' => $initializationVector, + ]); + + return $encodedContext; + } + + /** + * Return key for encryption. + * + * @return string + * @throws LocalizedException + */ + private function getKey() + { + $token = $this->analyticsToken->getToken(); + if (!$token) { + throw new LocalizedException(__('Encryption key can\'t be empty.')); + } + return hash('sha256', $token); + } + + /** + * Return established cipher method. + * + * @return string + */ + private function getCipherMethod() + { + return $this->cipherMethod; + } + + /** + * Return each time generated random initialization vector which depends on the cipher method. + * + * @return string + */ + private function getInitializationVector() + { + $ivSize = openssl_cipher_iv_length($this->getCipherMethod()); + return openssl_random_pseudo_bytes($ivSize); + } + + /** + * Check that cipher method is allowed for encryption. + * + * @param string $cipherMethod + * @return bool + */ + private function validateCipherMethod($cipherMethod) + { + $methods = openssl_get_cipher_methods(); + return (false !== array_search($cipherMethod, $methods)); + } +} diff --git a/app/code/Magento/Analytics/Model/EncodedContext.php b/app/code/Magento/Analytics/Model/EncodedContext.php new file mode 100644 index 0000000000000..358765ea79347 --- /dev/null +++ b/app/code/Magento/Analytics/Model/EncodedContext.php @@ -0,0 +1,52 @@ +content = $content; + $this->initializationVector = $initializationVector; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @return string + */ + public function getInitializationVector() + { + return $this->initializationVector; + } +} diff --git a/app/code/Magento/Analytics/Model/ExportDataHandler.php b/app/code/Magento/Analytics/Model/ExportDataHandler.php new file mode 100644 index 0000000000000..3f451e8a07872 --- /dev/null +++ b/app/code/Magento/Analytics/Model/ExportDataHandler.php @@ -0,0 +1,205 @@ +filesystem = $filesystem; + $this->archive = $archive; + $this->reportWriter = $reportWriter; + $this->cryptographer = $cryptographer; + $this->fileRecorder = $fileRecorder; + } + + /** + * Execute collecting new data for MBI. + * + * @return bool + */ + public function prepareExportData() + { + try { + $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP); + + $this->prepareDirectory($tmpDirectory, $this->getTmpFilesDirRelativePath()); + $this->reportWriter->write($tmpDirectory, $this->getTmpFilesDirRelativePath()); + + $tmpFilesDirectoryAbsolutePath = $this->validateSource($tmpDirectory, $this->getTmpFilesDirRelativePath()); + $archiveAbsolutePath = $this->prepareFileDirectory($tmpDirectory, $this->getArchiveRelativePath()); + $this->pack( + $tmpFilesDirectoryAbsolutePath, + $archiveAbsolutePath + ); + + $this->validateSource($tmpDirectory, $this->getArchiveRelativePath()); + $this->fileRecorder->recordNewFile( + $this->cryptographer->encode($tmpDirectory->readFile($this->getArchiveRelativePath())) + ); + } finally { + $tmpDirectory->delete($this->getTmpFilesDirRelativePath()); + $tmpDirectory->delete($this->getArchiveRelativePath()); + } + + return true; + } + + /** + * Return relative path to a directory for temporary files with reports data. + * + * @return string + */ + private function getTmpFilesDirRelativePath() + { + return $this->subdirectoryPath . 'tmp/'; + } + + /** + * Return relative path to a directory for an archive. + * + * @return string + */ + private function getArchiveRelativePath() + { + return $this->subdirectoryPath . $this->archiveName; + } + + /** + * Clean up a directory. + * + * @param WriteInterface $directory + * @param string $path + * @return string + */ + private function prepareDirectory(WriteInterface $directory, $path) + { + $directory->delete($path); + + return $directory->getAbsolutePath($path); + } + + /** + * Remove a file and a create parent directory a file. + * + * @param WriteInterface $directory + * @param string $path + * @return string + */ + private function prepareFileDirectory(WriteInterface $directory, $path) + { + $directory->delete($path); + if (dirname($path) !== '.') { + $directory->create(dirname($path)); + } + + return $directory->getAbsolutePath($path); + } + + /** + * Packing data into an archive. + * + * @param string $source + * @param string $destination + * @return bool + */ + private function pack($source, $destination) + { + $this->archive->pack( + $source, + $destination, + is_dir($source) ?: false + ); + + return true; + } + + /** + * Validate that data source exist. + * + * Return absolute path in a validated data source. + * + * @param WriteInterface $directory + * @param string $path + * @return string + * @throws LocalizedException If source is not exist. + */ + private function validateSource(WriteInterface $directory, $path) + { + if (!$directory->isExist($path)) { + throw new LocalizedException(__('Source "%1" is not exist', $directory->getAbsolutePath($path))); + } + + return $directory->getAbsolutePath($path); + } +} diff --git a/app/code/Magento/Analytics/Model/FileInfo.php b/app/code/Magento/Analytics/Model/FileInfo.php new file mode 100644 index 0000000000000..b7c38709944d3 --- /dev/null +++ b/app/code/Magento/Analytics/Model/FileInfo.php @@ -0,0 +1,52 @@ +path = $path; + $this->initializationVector = $initializationVector; + } + + /** + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * @return string + */ + public function getInitializationVector() + { + return $this->initializationVector; + } +} diff --git a/app/code/Magento/Analytics/Model/FileInfoManager.php b/app/code/Magento/Analytics/Model/FileInfoManager.php new file mode 100644 index 0000000000000..a55df9070a0d0 --- /dev/null +++ b/app/code/Magento/Analytics/Model/FileInfoManager.php @@ -0,0 +1,122 @@ +flagManager = $flagManager; + $this->fileInfoFactory = $fileInfoFactory; + } + + /** + * Save FileInfo object. + * + * @param FileInfo $fileInfo + * @return bool + * @throws LocalizedException + */ + public function save(FileInfo $fileInfo) + { + $parameters = []; + $parameters['initializationVector'] = $fileInfo->getInitializationVector(); + $parameters['path'] = $fileInfo->getPath(); + + $emptyParameters = array_diff($parameters, array_filter($parameters)); + if ($emptyParameters) { + throw new LocalizedException( + __('These arguments can\'t be empty "%1"', implode(', ', array_keys($emptyParameters))) + ); + } + + foreach ($this->encodedParameters as $encodedParameter) { + $parameters[$encodedParameter] = $this->encodeValue($parameters[$encodedParameter]); + } + + $this->flagManager->saveFlag($this->flagCode, $parameters); + + return true; + } + + /** + * Load FileInfo object. + * + * @return FileInfo + */ + public function load() + { + $parameters = $this->flagManager->getFlagData($this->flagCode) ?: []; + + $encodedParameters = array_intersect($this->encodedParameters, array_keys($parameters)); + foreach ($encodedParameters as $encodedParameter) { + $parameters[$encodedParameter] = $this->decodeValue($parameters[$encodedParameter]); + } + + $fileInfo = $this->fileInfoFactory->create($parameters); + + return $fileInfo; + } + + /** + * Encode value. + * + * @param string $value + * @return string + */ + private function encodeValue($value) + { + return base64_encode($value); + } + + /** + * Decode value. + * + * @param string $value + * @return string + */ + private function decodeValue($value) + { + return base64_decode($value); + } +} diff --git a/app/code/Magento/Analytics/Model/FileRecorder.php b/app/code/Magento/Analytics/Model/FileRecorder.php new file mode 100644 index 0000000000000..c1ef8da247147 --- /dev/null +++ b/app/code/Magento/Analytics/Model/FileRecorder.php @@ -0,0 +1,137 @@ +fileInfoManager = $fileInfoManager; + $this->fileInfoFactory = $fileInfoFactory; + $this->filesystem = $filesystem; + } + + /** + * Save new encrypted file, register it and remove old registered file. + * + * @param EncodedContext $encodedContext + * @return bool + */ + public function recordNewFile(EncodedContext $encodedContext) + { + $directory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + + $fileRelativePath = $this->getFileRelativePath(); + $directory->writeFile($fileRelativePath, $encodedContext->getContent()); + + $fileInfo = $this->fileInfoManager->load(); + $this->registerFile($encodedContext, $fileRelativePath); + $this->removeOldFile($fileInfo, $directory); + + return true; + + } + + /** + * Return relative path to encoded file. + * + * @return string + */ + private function getFileRelativePath() + { + return $this->fileSubdirectoryPath . hash('sha256', time()) + . '/' . $this->encodedFileName; + } + + /** + * Register encoded file. + * + * @param EncodedContext $encodedContext + * @param string $fileRelativePath + * @return bool + */ + private function registerFile(EncodedContext $encodedContext, $fileRelativePath) + { + $newFileInfo = $this->fileInfoFactory->create( + [ + 'path' => $fileRelativePath, + 'initializationVector' => $encodedContext->getInitializationVector(), + ] + ); + $this->fileInfoManager->save($newFileInfo); + + return true; + } + + /** + * Remove previously registered file. + * + * @param FileInfo $fileInfo + * @param WriteInterface $directory + * @return bool + */ + private function removeOldFile(FileInfo $fileInfo, WriteInterface $directory) + { + if (!$fileInfo->getPath()) { + return true; + } + + $directory->delete($fileInfo->getPath()); + + $directoryName = dirname($fileInfo->getPath()); + if ($directoryName !== '.') { + $directory->delete($directoryName); + } + + return true; + } +} diff --git a/app/code/Magento/Analytics/Model/FlagManager.php b/app/code/Magento/Analytics/Model/FlagManager.php new file mode 100644 index 0000000000000..47c8f0a0da2dd --- /dev/null +++ b/app/code/Magento/Analytics/Model/FlagManager.php @@ -0,0 +1,93 @@ +flagFactory = $flagFactory; + $this->flagResource = $flagResource; + } + + /** + * Return raw data from flag + * @param string $flagCode + * @return mixed + */ + public function getFlagData($flagCode) + { + return $this->getFlagObject($flagCode)->getFlagData(); + } + + /** + * Save flag by code + * @param string $flagCode + * @param mixed $value + * @return bool + */ + public function saveFlag($flagCode, $value) + { + $flag = $this->getFlagObject($flagCode); + $flag->setFlagData($value); + $this->flagResource->save($flag); + return true; + } + + /** + * Delete flag by code + * + * @param string $flagCode + * @return bool + */ + public function deleteFlag($flagCode) + { + $flag = $this->getFlagObject($flagCode); + if ($flag->getId()) { + $this->flagResource->delete($flag); + } + return true; + } + + /** + * Returns flag object + * + * @param string $flagCode + * @return Flag + */ + private function getFlagObject($flagCode) + { + /** @var Flag $flag */ + $flag = $this->flagFactory + ->create(['data' => ['flag_code' => $flagCode]]); + $this->flagResource->load($flag, $flagCode, 'flag_code'); + return $flag; + } +} diff --git a/app/code/Magento/Analytics/Model/IntegrationManager.php b/app/code/Magento/Analytics/Model/IntegrationManager.php new file mode 100644 index 0000000000000..bf625f03dc69a --- /dev/null +++ b/app/code/Magento/Analytics/Model/IntegrationManager.php @@ -0,0 +1,126 @@ +integrationService = $integrationService; + $this->config = $config; + $this->oauthService = $oauthService; + } + + /** + * Activate predefined integration user + * + * @return bool + * @throws NoSuchEntityException + */ + public function activateIntegration() + { + $integration = $this->integrationService->findByName( + $this->config->getConfigDataValue('analytics/integration_name') + ); + if (!$integration->getId()) { + throw new NoSuchEntityException(__('Cannot find predefined integration user!')); + } + $integrationData = $this->getIntegrationData(Integration::STATUS_ACTIVE); + $integrationData['integration_id'] = $integration->getId(); + $this->integrationService->update($integrationData); + return true; + } + + /** + * This method execute Generate Token command and enable integration + * + * @return bool|string + */ + public function generateToken() + { + $consumerId = $this->generateIntegration()->getConsumerId(); + $accessToken = $this->oauthService->getAccessToken($consumerId); + if (!$accessToken && $this->oauthService->createAccessToken($consumerId, true)) { + $accessToken = $this->oauthService->getAccessToken($consumerId); + } + return $accessToken; + } + + /** + * Returns consumer Id for MA integration user + * + * @return \Magento\Integration\Model\Integration + */ + private function generateIntegration() + { + $integration = $this->integrationService->findByName( + $this->config->getConfigDataValue('analytics/integration_name') + ); + if (!$integration->getId()) { + $integration = $this->integrationService->create($this->getIntegrationData()); + } + return $integration; + } + + /** + * Returns default attributes for MA integration user + * + * @param int $status + * @return array + */ + private function getIntegrationData($status = Integration::STATUS_INACTIVE) + { + $integrationData = [ + 'name' => $this->config->getConfigDataValue('analytics/integration_name'), + 'status' => $status, + 'all_resources' => false, + 'resource' => [ + 'Magento_Analytics::analytics', + 'Magento_Analytics::analytics_api' + ], + ]; + return $integrationData; + } +} diff --git a/app/code/Magento/Analytics/Model/Link.php b/app/code/Magento/Analytics/Model/Link.php new file mode 100644 index 0000000000000..cbbbca93d6c47 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Link.php @@ -0,0 +1,60 @@ +url; + } + + /** + * @return string + */ + public function getInitializationVector() + { + return $this->initializationVector; + } + + /** + * @param string $url + * @return void + */ + public function setUrl($url) + { + $this->url = $url; + } + + /** + * @param string $initializationVector + * @return void + */ + public function setInitializationVector($initializationVector) + { + $this->initializationVector = $initializationVector; + } +} diff --git a/app/code/Magento/Analytics/Model/LinkProvider.php b/app/code/Magento/Analytics/Model/LinkProvider.php new file mode 100644 index 0000000000000..2d4e7dad04073 --- /dev/null +++ b/app/code/Magento/Analytics/Model/LinkProvider.php @@ -0,0 +1,67 @@ +linkInterfaceFactory = $linkInterfaceFactory; + $this->fileInfoManager = $fileInfoManager; + $this->storeManager = $storeManager; + } + + /** + * @inheritdoc + */ + public function get() + { + $fileInfo = $this->fileInfoManager->load(); + if ($fileInfo->getPath() === null || $fileInfo->getInitializationVector() === null) { + throw new Exception(__('File is not ready yet.'), 0, Exception::HTTP_NOT_FOUND); + } + $link = $this->linkInterfaceFactory->create(); + $link->setUrl( + $this->storeManager->getStore()->getBaseUrl( + UrlInterface::URL_TYPE_MEDIA + ) . $fileInfo->getPath() + ); + $link->setInitializationVector(base64_encode($fileInfo->getInitializationVector())); + return $link; + } +} diff --git a/app/code/Magento/Analytics/Model/NotificationTime.php b/app/code/Magento/Analytics/Model/NotificationTime.php new file mode 100644 index 0000000000000..4df4cc970ee6d --- /dev/null +++ b/app/code/Magento/Analytics/Model/NotificationTime.php @@ -0,0 +1,65 @@ +flagManager = $flagManager; + } + + /** + * Stores last notification time + * + * @param string $value + * @return bool + */ + public function storeLastTimeNotification($value) + { + return $this->flagManager->saveFlag(self::NOTIFICATION_TIME, $value); + } + + /** + * Returns last time when merchant was notified about Analytic services + * + * @return int + */ + public function getLastTimeNotification() + { + return $this->flagManager->getFlagData(self::NOTIFICATION_TIME); + } + + /** + * Remove last notification time flag. + * + * @return bool + */ + public function unsetLastTimeNotificationValue() + { + return $this->flagManager->deleteFlag(self::NOTIFICATION_TIME); + } +} diff --git a/app/code/Magento/Analytics/Model/Plugin/BaseUrlConfigPlugin.php b/app/code/Magento/Analytics/Model/Plugin/BaseUrlConfigPlugin.php new file mode 100644 index 0000000000000..e4d4441edbbf4 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Plugin/BaseUrlConfigPlugin.php @@ -0,0 +1,101 @@ +flagManager = $flagManager; + $this->subscriptionStatusProvider = $subscriptionStatusProvider; + $this->configWriter = $configWriter; + } + + /** + * Sets update analytics cron job if base url was changed. + * + * @param \Magento\Config\Model\Config\Backend\Baseurl $subject + * @param \Magento\Config\Model\Config\Backend\Baseurl $result + * @return \Magento\Config\Model\Config\Backend\Baseurl + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterAfterSave( + \Magento\Config\Model\Config\Backend\Baseurl $subject, + \Magento\Config\Model\Config\Backend\Baseurl $result + ) { + if (!$result->isValueChanged()) { + return $result; + } + + if ($this->isPluginApplicable($result)) { + $this->flagManager->saveFlag(static::OLD_BASE_URL_FLAG_CODE, $result->getOldValue()); + $this->configWriter->save(self::UPDATE_CRON_STRING_PATH, $this->cronExpression); + } + + return $result; + } + + /** + * @param \Magento\Config\Model\Config\Backend\Baseurl $result + * + * @return bool + */ + private function isPluginApplicable(\Magento\Config\Model\Config\Backend\Baseurl $result) + { + return $result->getData('path') === \Magento\Store\Model\Store::XML_PATH_SECURE_BASE_URL + && $this->subscriptionStatusProvider->getStatus() === SubscriptionStatusProvider::ENABLED; + } +} diff --git a/app/code/Magento/Analytics/Model/ProviderFactory.php b/app/code/Magento/Analytics/Model/ProviderFactory.php new file mode 100644 index 0000000000000..b146c79ccb0b7 --- /dev/null +++ b/app/code/Magento/Analytics/Model/ProviderFactory.php @@ -0,0 +1,39 @@ +objectManager = $objectManager; + } + + /** + * @param string $providerName + * @return object + */ + public function create($providerName) + { + return $this->objectManager->get($providerName); + } +} diff --git a/app/code/Magento/Analytics/Model/ReportUrlProvider.php b/app/code/Magento/Analytics/Model/ReportUrlProvider.php new file mode 100644 index 0000000000000..e408106796dc4 --- /dev/null +++ b/app/code/Magento/Analytics/Model/ReportUrlProvider.php @@ -0,0 +1,75 @@ +analyticsToken = $analyticsToken; + $this->otpRequest = $otpRequest; + $this->config = $config; + } + + /** + * Provide URL on resource with reports. + * + * @return string + */ + public function getUrl() + { + $url = $this->config->getValue($this->urlReportConfigPath); + if ($this->analyticsToken->isTokenExist()) { + $otp = $this->otpRequest->call(); + if ($otp) { + $query = http_build_query(['otp' => $otp], '', '&'); + $url .= '?' . $query; + } + } + + return $url; + } +} diff --git a/app/code/Magento/Analytics/Model/ReportWriter.php b/app/code/Magento/Analytics/Model/ReportWriter.php new file mode 100644 index 0000000000000..80b1210f9677a --- /dev/null +++ b/app/code/Magento/Analytics/Model/ReportWriter.php @@ -0,0 +1,102 @@ +config = $config; + $this->reportValidator = $reportValidator; + $this->providerFactory = $providerFactory; + } + + /** + * {@inheritdoc} + */ + public function write(WriteInterface $directory, $path) + { + $errorsList = []; + foreach ($this->config->get() as $file) { + $provider = reset($file['providers']); + if (isset($provider['parameters']['name'])) { + $error = $this->reportValidator->validate($provider['parameters']['name']); + if ($error) { + $errorsList[] = $error; + continue; + } + } + /** @var $providerObject */ + $providerObject = $this->providerFactory->create($provider['class']); + $fileName = $provider['parameters'] ? $provider['parameters']['name'] : $provider['name']; + $fileFullPath = $path . $fileName . '.csv'; + $fileData = $providerObject->getReport(...array_values($provider['parameters'])); + $stream = $directory->openFile($fileFullPath, 'w+'); + $stream->lock(); + $headers = []; + foreach ($fileData as $row) { + if (!$headers) { + $headers = array_keys($row); + $stream->writeCsv($headers); + + } + $stream->writeCsv($row); + } + $stream->unlock(); + $stream->close(); + } + if ($errorsList) { + $errorStream = $directory->openFile($path . $this->errorsFileName, 'w+'); + foreach ($errorsList as $error) { + $errorStream->lock(); + $errorStream->writeCsv($error); + $errorStream->unlock(); + } + $errorStream->close(); + } + + return true; + } +} diff --git a/app/code/Magento/Analytics/Model/ReportWriterInterface.php b/app/code/Magento/Analytics/Model/ReportWriterInterface.php new file mode 100644 index 0000000000000..20a8e5db4ffb8 --- /dev/null +++ b/app/code/Magento/Analytics/Model/ReportWriterInterface.php @@ -0,0 +1,28 @@ +moduleManager = $moduleManager; + } + + /** + * Returns module with module status + * + * @return array + */ + public function current() + { + $current = parent::current(); + if (is_array($current) && isset($current['module_name'])) { + $current['status'] = + $this->moduleManager->isEnabled($current['module_name']) == 1 ? 'Enabled' : "Disabled"; + } + return $current; + } +} diff --git a/app/code/Magento/Analytics/Model/StoreConfigurationProvider.php b/app/code/Magento/Analytics/Model/StoreConfigurationProvider.php new file mode 100644 index 0000000000000..314225dc3d346 --- /dev/null +++ b/app/code/Magento/Analytics/Model/StoreConfigurationProvider.php @@ -0,0 +1,102 @@ +scopeConfig = $scopeConfig; + $this->configPaths = $configPaths; + $this->storeManager = $storeManager; + } + + /** + * Generates report using config paths from di.xml + * For each website and store + * @return \IteratorIterator + */ + public function getReport() + { + $configReport = $this->generateReportForScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0); + + /** @var WebsiteInterface $website */ + foreach ($this->storeManager->getWebsites() as $website) { + $configReport = array_merge( + $this->generateReportForScope(ScopeInterface::SCOPE_WEBSITES, $website->getId()), + $configReport + ); + } + + /** @var StoreInterface $store */ + foreach ($this->storeManager->getStores() as $store) { + $configReport = array_merge( + $this->generateReportForScope(ScopeInterface::SCOPE_STORES, $store->getId()), + $configReport + ); + } + return new \IteratorIterator(new \ArrayIterator($configReport)); + } + + /** + * Creates report from config for scope type and scope id. + * + * @param string $scope + * @param int $scopeId + * @return array + */ + private function generateReportForScope($scope, $scopeId) + { + $report = []; + foreach ($this->configPaths as $configPath) { + $report[] = [ + "config_path" => $configPath, + "scope" => $scope, + "scope_id" => $scopeId, + "value" => $this->scopeConfig->getValue( + $configPath, + $scope, + $scopeId + ) + ]; + } + return $report; + } +} diff --git a/app/code/Magento/Analytics/Model/Subscription.php b/app/code/Magento/Analytics/Model/Subscription.php new file mode 100644 index 0000000000000..e59fbcb1c3d70 --- /dev/null +++ b/app/code/Magento/Analytics/Model/Subscription.php @@ -0,0 +1,108 @@ +configValueFactory = $configValueFactory; + $this->configStructure = $configStructure; + $this->configValueResource = $configValueResource; + $this->reinitableConfig = $reinitableConfig; + } + + /** + * Set subscription enabled config value. + * + * @return boolean + */ + public function enable() + { + /** @var Field $field */ + $field = $this->configStructure->getElement($this->enabledConfigStructurePath); + /** @var Value $configValue */ + $configValue = $field->hasBackendModel() + ? $field->getBackendModel() + : $this->configValueFactory->create(); + $configPath = $field->getConfigPath() ?: $this->enabledConfigStructurePath; + + $this->configValueResource + ->load($configValue, $configPath, 'path'); + + $configValue->setValue($this->yesValueDropdown); + $configValue->setPath($configPath); + + $this->configValueResource + ->save($configValue); + + $this->reinitableConfig->reinit(); + + return true; + } +} diff --git a/app/code/Magento/Analytics/Model/SubscriptionStatusProvider.php b/app/code/Magento/Analytics/Model/SubscriptionStatusProvider.php new file mode 100644 index 0000000000000..80dd2f7cc1bee --- /dev/null +++ b/app/code/Magento/Analytics/Model/SubscriptionStatusProvider.php @@ -0,0 +1,109 @@ +systemConfig = $systemConfig; + $this->analyticsToken = $analyticsToken; + } + + /** + * Statuses: + * + * Enabled - if subscription is enabled and MA token was received; + * Pending - if subscription is enabled and MA token was not received; + * Disabled - if subscription is not enabled. + * + * @return string + */ + public function getStatus() + { + $checkboxState = $this->systemConfig->get('default/analytics/subscription/enabled'); + return $this->resolveStatus($checkboxState); + } + + /** + * Resolves subscription status depending on + * subscription config value (enabled, disabled). + * + * @param bool $isSubscriptionEnabled + * + * @return string + */ + private function resolveStatus($isSubscriptionEnabled) + { + if (!$isSubscriptionEnabled) { + return static::DISABLED; + } + + $status = static::PENDING; + + if ($this->analyticsToken->isTokenExist()) { + $status = static::ENABLED; + } + + return $status; + } + + /** + * Retrieve status for subscription that enabled in config. + * + * @return string + */ + public function getStatusForEnabledSubscription() + { + return $this->resolveStatus(true); + } + + /** + * Retrieve status for subscription that disabled in config. + * + * @return string + */ + public function getStatusForDisabledSubscription() + { + return $this->resolveStatus(false); + } +} diff --git a/app/code/Magento/Analytics/README.md b/app/code/Magento/Analytics/README.md new file mode 100644 index 0000000000000..cb8fdc3287190 --- /dev/null +++ b/app/code/Magento/Analytics/README.md @@ -0,0 +1,12 @@ +## Overview + +The Magento_Analytics module provides integration with +[Magento Business Intelligence](https://magento.com/products/business-intelligence). Admin user can subscribe to +Advanced Analytics, navigate to Advanced Analytics reports and Magento Business Intelligence subscription page. + +The Magento_Analytics module adds: + +- Advanced Analytics subscription pop-up +- backend menu link to Magento Business Intelligence subscription page under Reports menu +- Magento Analytics configuration page in Stores-Configuration->General menu +- configuration to build a data collection for BI system \ No newline at end of file diff --git a/app/code/Magento/Analytics/ReportXml/Config.php b/app/code/Magento/Analytics/ReportXml/Config.php new file mode 100644 index 0000000000000..53d4cee09191d --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/Config.php @@ -0,0 +1,43 @@ +data = $data; + } + + /** + * Returns config value by name + * + * @param string $queryName + * @return array + */ + public function get($queryName) + { + return $this->data->get($queryName); + } +} diff --git a/app/code/Magento/Analytics/ReportXml/Config/Converter/Xml.php b/app/code/Magento/Analytics/ReportXml/Config/Converter/Xml.php new file mode 100644 index 0000000000000..c0152ed262c52 --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/Config/Converter/Xml.php @@ -0,0 +1,61 @@ +hasAttributes()) { + $attrs = $source->attributes; + foreach ($attrs as $attr) { + $result[$attr->name] = $attr->value; + } + } + if ($source->hasChildNodes()) { + $children = $source->childNodes; + if ($children->length == 1) { + $child = $children->item(0); + if ($child->nodeType == XML_TEXT_NODE) { + $result['_value'] = $child->nodeValue; + return count($result) == 1 ? $result['_value'] : $result; + } + } + foreach ($children as $child) { + if ($child instanceof \DOMCharacterData) { + continue; + } + $result[$child->nodeName][] = $this->convertNode($child); + } + } + return $result; + } + + /** + * Converts XML document into corresponding array. + * + * @param \DOMDocument $source + * @return array + */ + public function convert($source) + { + return $this->convertNode($source); + } +} diff --git a/app/code/Magento/Analytics/ReportXml/Config/Data.php b/app/code/Magento/Analytics/ReportXml/Config/Data.php new file mode 100644 index 0000000000000..1d3f69dc30373 --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/Config/Data.php @@ -0,0 +1,29 @@ +readers = $readers; + $this->mapper = $mapper; + } + + /** + * Reads configuration according to the given scope. + * + * @param string|null $scope + * @return array + */ + public function read($scope = null) + { + $data = []; + foreach ($this->readers as $reader) { + $data = array_merge_recursive($data, $reader->read($scope)); + } + return $this->mapper->execute($data); + } +} diff --git a/app/code/Magento/Analytics/ReportXml/Config/Reader/Xml.php b/app/code/Magento/Analytics/ReportXml/Config/Reader/Xml.php new file mode 100644 index 0000000000000..2ff333cbb81ef --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/Config/Reader/Xml.php @@ -0,0 +1,56 @@ + 'name' + ]; + + /** + * @param \Magento\Framework\Config\FileResolverInterface $fileResolver + * @param \Magento\Analytics\ReportXml\Config\Converter\Xml $converter + * @param \Magento\Analytics\ReportXml\Config\SchemaLocator $schemaLocator + * @param \Magento\Framework\Config\ValidationStateInterface $validationState + * @param string $fileName + * @param array $idAttributes + * @param string $domDocumentClass + * @param string $defaultScope + */ + public function __construct( + \Magento\Framework\Config\FileResolverInterface $fileResolver, + \Magento\Analytics\ReportXml\Config\Converter\Xml $converter, + \Magento\Analytics\ReportXml\Config\SchemaLocator $schemaLocator, + \Magento\Framework\Config\ValidationStateInterface $validationState, + $fileName = 'reports.xml', + $idAttributes = [], + $domDocumentClass = \Magento\Framework\Config\Dom::class, + $defaultScope = 'global' + ) { + parent::__construct( + $fileResolver, + $converter, + $schemaLocator, + $validationState, + $fileName, + $idAttributes, + $domDocumentClass, + $defaultScope + ); + } +} diff --git a/app/code/Magento/Analytics/ReportXml/Config/SchemaLocator.php b/app/code/Magento/Analytics/ReportXml/Config/SchemaLocator.php new file mode 100644 index 0000000000000..0a5a49e9b9744 --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/Config/SchemaLocator.php @@ -0,0 +1,51 @@ +urnResolver = $urnResolver; + } + + /** + * {@inheritdoc} + */ + public function getSchema() + { + return $this->urnResolver->getRealPath($this->realPath); + } + + /** + * {@inheritdoc} + */ + public function getPerFileSchema() + { + return null; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/ConfigInterface.php b/app/code/Magento/Analytics/ReportXml/ConfigInterface.php new file mode 100644 index 0000000000000..f706321aa767c --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/ConfigInterface.php @@ -0,0 +1,23 @@ +resourceConnection = $resourceConnection; + $this->objectManager = $objectManager; + } + + /** + * Creates one-time connection for export + * + * @param string $connectionName + * @return AdapterInterface + */ + public function getConnection($connectionName) + { + $connection = $this->resourceConnection->getConnection($connectionName); + $connectionClassName = get_class($connection); + $configData = $connection->getConfig(); + $configData['use_buffered_query'] = false; + unset($configData['persistent']); + return $this->objectManager->create( + $connectionClassName, + [ + 'config' => $configData + ] + ); + } +} diff --git a/app/code/Magento/Analytics/ReportXml/DB/Assembler/AssemblerInterface.php b/app/code/Magento/Analytics/ReportXml/DB/Assembler/AssemblerInterface.php new file mode 100644 index 0000000000000..04764b92e91e2 --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/DB/Assembler/AssemblerInterface.php @@ -0,0 +1,27 @@ +conditionResolver = $conditionResolver; + $this->nameResolver = $nameResolver; + } + + /** + * Assembles WHERE conditions + * + * @param SelectBuilder $selectBuilder + * @param array $queryConfig + * @return SelectBuilder + */ + public function assemble(SelectBuilder $selectBuilder, $queryConfig) + { + if (!isset($queryConfig['source']['filter'])) { + return $selectBuilder; + } + $filters = $this->conditionResolver->getFilter( + $selectBuilder, + $queryConfig['source']['filter'], + $this->nameResolver->getAlias($queryConfig['source']) + ); + $selectBuilder->setFilters(array_merge_recursive($selectBuilder->getFilters(), [$filters])); + return $selectBuilder; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/DB/Assembler/FromAssembler.php b/app/code/Magento/Analytics/ReportXml/DB/Assembler/FromAssembler.php new file mode 100644 index 0000000000000..2ae835bb3344f --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/DB/Assembler/FromAssembler.php @@ -0,0 +1,63 @@ +nameResolver = $nameResolver; + $this->columnsResolver = $columnsResolver; + } + + /** + * Assembles FROM condition + * + * @param SelectBuilder $selectBuilder + * @param array $queryConfig + * @return SelectBuilder + */ + public function assemble(SelectBuilder $selectBuilder, $queryConfig) + { + $selectBuilder->setFrom( + [ + $this->nameResolver->getAlias($queryConfig['source']) + => $this->nameResolver->getName($queryConfig['source']) + ] + ); + $columns = $this->columnsResolver->getColumns($selectBuilder, $queryConfig['source']); + $selectBuilder->setColumns(array_merge($selectBuilder->getColumns(), $columns)); + return $selectBuilder; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/DB/Assembler/JoinAssembler.php b/app/code/Magento/Analytics/ReportXml/DB/Assembler/JoinAssembler.php new file mode 100644 index 0000000000000..a457b8bd126a1 --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/DB/Assembler/JoinAssembler.php @@ -0,0 +1,105 @@ +conditionResolver = $conditionResolver; + $this->nameResolver = $nameResolver; + $this->columnsResolver = $columnsResolver; + } + + /** + * Assembles JOIN conditions + * + * @param SelectBuilder $selectBuilder + * @param array $queryConfig + * @return SelectBuilder + */ + public function assemble(SelectBuilder $selectBuilder, $queryConfig) + { + if (!isset($queryConfig['source']['link-source'])) { + return $selectBuilder; + } + $joins = []; + $filters = $selectBuilder->getFilters(); + + $sourceAlias = $this->nameResolver->getAlias($queryConfig['source']); + + foreach ($queryConfig['source']['link-source'] as $join) { + $joinAlias = $this->nameResolver->getAlias($join); + + $joins[$joinAlias] = [ + 'link-type' => isset($join['link-type']) ? $join['link-type'] : 'left', + 'table' => [ + $joinAlias => $this->nameResolver->getName($join) + ], + 'condition' => $this->conditionResolver->getFilter( + $selectBuilder, + $join['using'], + $joinAlias, + $sourceAlias + ) + ]; + if (isset($join['filter'])) { + $filters = array_merge( + $filters, + [ + $this->conditionResolver->getFilter( + $selectBuilder, + $join['filter'], + $joinAlias, + $sourceAlias + ) + ] + ); + } + $columns = $this->columnsResolver->getColumns($selectBuilder, isset($join['attribute']) ? $join : []); + $selectBuilder->setColumns(array_merge($selectBuilder->getColumns(), $columns)); + } + $selectBuilder->setFilters($filters); + $selectBuilder->setJoins(array_merge($selectBuilder->getJoins(), $joins)); + return $selectBuilder; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/DB/ColumnsResolver.php b/app/code/Magento/Analytics/ReportXml/DB/ColumnsResolver.php new file mode 100644 index 0000000000000..57f08c236116a --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/DB/ColumnsResolver.php @@ -0,0 +1,69 @@ +nameResolver = $nameResolver; + } + + /** + * Set columns list to SelectBuilder + * + * @param SelectBuilder $selectBuilder + * @param array $entityConfig + * @return array + */ + public function getColumns(SelectBuilder $selectBuilder, $entityConfig) + { + if (!isset($entityConfig['attribute'])) { + return []; + } + $group = []; + $columns = $selectBuilder->getColumns(); + foreach ($entityConfig['attribute'] as $attributeData) { + $columnAlias = $this->nameResolver->getAlias($attributeData); + $tableAlias = $this->nameResolver->getAlias($entityConfig); + $columnName = $this->nameResolver->getName($attributeData); + if (isset($attributeData['function'])) { + $prefix = ''; + if (isset($attributeData['distinct']) && $attributeData['distinct'] == true) { + $prefix = ' DISTINCT '; + } + $expression = new \Zend_Db_Expr( + strtoupper($attributeData['function']) . '(' . $prefix . $tableAlias . '.' . $columnName . ')' + ); + } else { + $expression = $tableAlias . '.' . $columnName; + } + $columns[$columnAlias] = $expression; + if (isset($attributeData['group'])) { + $group[$columnAlias] = $expression; + } + + } + $selectBuilder->setGroup(array_merge($selectBuilder->getGroup(), $group)); + return $columns; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/DB/ConditionResolver.php b/app/code/Magento/Analytics/ReportXml/DB/ConditionResolver.php new file mode 100644 index 0000000000000..9eb506cbb105f --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/DB/ConditionResolver.php @@ -0,0 +1,163 @@ + '%1$s = %2$s', + 'neq' => '%1$s != %2$s', + 'like' => '%1$s LIKE %2$s', + 'nlike' => '%1$s NOT LIKE %2$s', + 'in' => '%1$s IN(%2$s)', + 'nin' => '%1$s NOT IN(%2$s)', + 'notnull' => '%1$s IS NOT NULL', + 'null' => '%1$s IS NULL', + 'gt' => '%1$s > %2$s', + 'lt' => '%1$s < %2$s', + 'gteq' => '%1$s >= %2$s', + 'lteq' => '%1$s <= %2$s', + 'finset' => 'FIND_IN_SET(%2$s, %1$s)' + ]; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface + */ + private $connection; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * ConditionResolver constructor. + * @param ResourceConnection $resourceConnection + */ + public function __construct( + ResourceConnection $resourceConnection + ) { + $this->resourceConnection = $resourceConnection; + } + + /** + * Returns connection + * + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ + private function getConnection() + { + if (!$this->connection) { + $this->connection = $this->resourceConnection->getConnection(); + } + return $this->connection; + } + + /** + * Returns value for condition + * + * @param string $condition + * @param string $referencedEntity + * @return mixed|null|string|\Zend_Db_Expr + */ + private function getValue($condition, $referencedEntity) + { + $value = null; + $argument = isset($condition['_value']) ? $condition['_value'] : null; + if (!isset($condition['type'])) { + $condition['type'] = 'value'; + } + + switch ($condition['type']) { + case "value": + $value = $this->getConnection()->quote($argument); + break; + case "variable": + $value = new \Zend_Db_Expr($argument); + break; + case "identifier": + $value = $this->getConnection()->quoteIdentifier( + $referencedEntity ? $referencedEntity . '.' . $argument : $argument + ); + break; + } + return $value; + } + + /** + * Returns condition for WHERE + * + * @param SelectBuilder $selectBuilder + * @param string $tableName + * @param array $condition + * @param null|string $referencedEntity + * @return string + */ + private function getCondition(SelectBuilder $selectBuilder, $tableName, $condition, $referencedEntity = null) + { + $columns = $selectBuilder->getColumns(); + if (isset($columns[$condition['attribute']]) && $columns[$condition['attribute']] instanceof \Zend_Db_Expr) { + $expression = $columns[$condition['attribute']]; + } else { + $expression = $tableName . '.' . $condition['attribute']; + } + return sprintf( + $this->conditionMap[$condition['operator']], + $expression, + $this->getValue($condition, $referencedEntity) + ); + } + + /** + * Build WHERE condition + * + * @param SelectBuilder $selectBuilder + * @param array $filterConfig + * @param string $aliasName + * @param null|string $referencedAlias + * @return array + */ + public function getFilter(SelectBuilder $selectBuilder, $filterConfig, $aliasName, $referencedAlias = null) + { + $filtersParts = []; + foreach ($filterConfig as $filter) { + $glue = $filter['glue']; + $parts = []; + foreach ($filter['condition'] as $condition) { + if (isset($condition['type']) && $condition['type'] == 'variable') { + $selectBuilder->setParams(array_merge($selectBuilder->getParams(), [$condition['_value']])); + } + $parts[] = $this->getCondition( + $selectBuilder, + $aliasName, + $condition, + $referencedAlias + ); + } + if (isset($filter['filter'])) { + $parts[] = '(' . $this->getFilter( + $selectBuilder, + $filter['filter'], + $aliasName, + $referencedAlias + ) . ')'; + } + $filtersParts[] = '(' . implode(' ' . strtoupper($glue) . ' ', $parts) . ')'; + } + return implode(' AND ', $filtersParts); + } +} diff --git a/app/code/Magento/Analytics/ReportXml/DB/NameResolver.php b/app/code/Magento/Analytics/ReportXml/DB/NameResolver.php new file mode 100644 index 0000000000000..52d8177cbb8a9 --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/DB/NameResolver.php @@ -0,0 +1,40 @@ +getName($elementConfig); + if (isset($elementConfig['alias'])) { + $alias = $elementConfig['alias']; + } + return $alias; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/DB/ReportValidator.php b/app/code/Magento/Analytics/ReportXml/DB/ReportValidator.php new file mode 100644 index 0000000000000..303cd09262b0e --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/DB/ReportValidator.php @@ -0,0 +1,64 @@ +connectionFactory = $connectionFactory; + $this->queryFactory = $queryFactory; + } + + /** + * Tries to do query for provided report with limit 0 and return error information if it failed + * + * @param string $name + * @param SearchCriteriaInterface $criteria + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function validate($name, SearchCriteriaInterface $criteria = null) + { + $query = $this->queryFactory->create($name); + $connection = $this->connectionFactory->getConnection($query->getConnectionName()); + $query->getSelect()->limit(0); + try { + $connection->query($query->getSelect()); + } catch (\Zend_Db_Statement_Exception $e) { + return [$name, $e->getMessage()]; + } + + return []; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/DB/SelectBuilder.php b/app/code/Magento/Analytics/ReportXml/DB/SelectBuilder.php new file mode 100644 index 0000000000000..efe958fee315e --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/DB/SelectBuilder.php @@ -0,0 +1,289 @@ +resourceConnection = $resourceConnection; + } + + /** + * Get join condition + * + * @return array + */ + public function getJoins() + { + return $this->joins; + } + + /** + * Set joins conditions + * + * @param array $joins + * @return void + */ + public function setJoins($joins) + { + $this->joins = $joins; + } + + /** + * Get connection name + * + * @return string + */ + public function getConnectionName() + { + return $this->connectionName; + } + + /** + * Set connection name + * + * @param string $connectionName + * @return void + */ + public function setConnectionName($connectionName) + { + $this->connectionName = $connectionName; + } + + /** + * Get columns + * + * @return array + */ + public function getColumns() + { + return $this->columns; + } + + /** + * Set columns + * + * @param array $columns + * @return void + */ + public function setColumns($columns) + { + $this->columns = $columns; + } + + /** + * Get filters + * + * @return array + */ + public function getFilters() + { + return $this->filters; + } + + /** + * Set filters + * + * @param array $filters + * @return void + */ + public function setFilters($filters) + { + $this->filters = $filters; + } + + /** + * Get from condition + * + * @return array + */ + public function getFrom() + { + return $this->from; + } + + /** + * Set from condition + * + * @param array $from + * @return void + */ + public function setFrom($from) + { + $this->from = $from; + } + + /** + * Process JOIN conditions + * + * @param Select $select + * @param array $joinConfig + * @return Select + */ + private function processJoin(Select $select, $joinConfig) + { + switch ($joinConfig['link-type']) { + case 'left': + $select->joinLeft($joinConfig['table'], $joinConfig['condition'], []); + break; + case 'inner': + $select->joinInner($joinConfig['table'], $joinConfig['condition'], []); + break; + case 'right': + $select->joinRight($joinConfig['table'], $joinConfig['condition'], []); + break; + } + return $select; + } + + /** + * Creates Select object + * + * @return Select + */ + public function create() + { + $connection = $this->resourceConnection->getConnection($this->getConnectionName()); + $select = $connection->select(); + $select->from($this->getFrom(), []); + $select->columns($this->getColumns()); + foreach ($this->getFilters() as $filter) { + $select->where($filter); + } + foreach ($this->getJoins() as $joinConfig) { + $select = $this->processJoin($select, $joinConfig); + } + if (!empty($this->getGroup())) { + $select->group(implode(', ', $this->getGroup())); + } + return $select; + } + + /** + * Returns group + * + * @return array + */ + public function getGroup() + { + return $this->group; + } + + /** + * Set group + * + * @param array $group + * @return void + */ + public function setGroup($group) + { + $this->group = $group; + } + + /** + * Get parameters + * + * @return array + */ + public function getParams() + { + return $this->params; + } + + /** + * Set parameters + * + * @param array $params + * @return void + */ + public function setParams($params) + { + $this->params = $params; + } + + /** + * Get having condition + * + * @return array + */ + public function getHaving() + { + return $this->having; + } + + /** + * Set having condition + * + * @param array $having + * @return void + */ + public function setHaving($having) + { + $this->having = $having; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/DB/SelectBuilderFactory.php b/app/code/Magento/Analytics/ReportXml/DB/SelectBuilderFactory.php new file mode 100644 index 0000000000000..3fc12ccd0a39c --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/DB/SelectBuilderFactory.php @@ -0,0 +1,43 @@ +objectManager = $objectManager; + } + + /** + * Create class instance with specified parameters + * + * @param array $data + * @return SelectBuilder + */ + public function create(array $data = []) + { + return $this->objectManager->create(SelectBuilder::class, $data); + } +} diff --git a/app/code/Magento/Analytics/ReportXml/IteratorFactory.php b/app/code/Magento/Analytics/ReportXml/IteratorFactory.php new file mode 100644 index 0000000000000..2932d42228af2 --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/IteratorFactory.php @@ -0,0 +1,61 @@ +objectManager = $objectManager; + $this->defaultIteratorName = $defaultIteratorName; + } + + /** + * Creates instance of the result iterator with the query result as an input + * Result iterator can be changed through report configuration + * + * < ... + * + * Uses IteratorIterator by default + * + * @param \Traversable $result + * @param string|null $iteratorName + * @return \IteratorIterator + */ + public function create(\Traversable $result, $iteratorName = null) + { + return $this->objectManager->create( + $iteratorName ?: $this->defaultIteratorName, + [ + 'iterator' => $result + ] + ); + } +} diff --git a/app/code/Magento/Analytics/ReportXml/Query.php b/app/code/Magento/Analytics/ReportXml/Query.php new file mode 100644 index 0000000000000..2bdf2432600dd --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/Query.php @@ -0,0 +1,96 @@ +select = $select; + $this->connectionName = $connectionName; + $this->selectHydrator = $selectHydrator; + $this->config = $config; + } + + /** + * @return Select + */ + public function getSelect() + { + return $this->select; + } + + /** + * @return string + */ + public function getConnectionName() + { + return $this->connectionName; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * Specify data which should be serialized to JSON + * @link http://php.net/manual/en/jsonserializable.jsonserialize.php + * @return mixed data which can be serialized by json_encode, + * which is a value of any type other than a resource. + * @since 5.4.0 + */ + public function jsonSerialize() + { + return [ + 'connectionName' => $this->getConnectionName(), + 'select_parts' => $this->selectHydrator->extract($this->getSelect()), + 'config' => $this->getConfig() + ]; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/QueryFactory.php b/app/code/Magento/Analytics/ReportXml/QueryFactory.php new file mode 100644 index 0000000000000..001a35354cf0a --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/QueryFactory.php @@ -0,0 +1,142 @@ +config = $config; + $this->selectBuilderFactory = $selectBuilderFactory; + $this->assemblers = $assemblers; + $this->queryCache = $queryCache; + $this->objectManager = $objectManager; + $this->selectHydrator = $selectHydrator; + } + + /** + * Returns query connection name according to configuration + * + * @param string $queryConfig + * @return string + */ + private function getQueryConnectionName($queryConfig) + { + $connectionName = 'default'; + if (isset($queryConfig['connection'])) { + $connectionName = $queryConfig['connection']; + } + return $connectionName; + } + + /** + * Create query according to configuration settings + * + * @param string $queryName + * @return Query + */ + private function constructQuery($queryName) + { + $queryConfig = $this->config->get($queryName); + $selectBuilder = $this->selectBuilderFactory->create(); + $selectBuilder->setConnectionName($this->getQueryConnectionName($queryConfig)); + foreach ($this->assemblers as $assembler) { + $selectBuilder = $assembler->assemble($selectBuilder, $queryConfig); + } + $select = $selectBuilder->create(); + return $this->objectManager->create( + Query::class, + [ + 'select' => $select, + 'selectHydrator' => $this->selectHydrator, + 'connectionName' => $selectBuilder->getConnectionName(), + 'config' => $queryConfig + ] + ); + } + + /** + * Creates query by name + * + * @param string $queryName + * @return Query + */ + public function create($queryName) + { + $cached = $this->queryCache->load($queryName); + if ($cached) { + $queryData = json_decode($cached, true); + return $this->objectManager->create( + Query::class, + [ + 'select' => $this->selectHydrator->recreate($queryData['select_parts']), + 'selectHydrator' => $this->selectHydrator, + 'connectionName' => $queryData['connectionName'], + 'config' => $queryData['config'] + ] + ); + } + $query = $this->constructQuery($queryName); + $this->queryCache->save(json_encode($query), $queryName); + return $query; + } +} diff --git a/app/code/Magento/Analytics/ReportXml/ReportProvider.php b/app/code/Magento/Analytics/ReportXml/ReportProvider.php new file mode 100644 index 0000000000000..9a8a8ffab0861 --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/ReportProvider.php @@ -0,0 +1,79 @@ +queryFactory = $queryFactory; + $this->connectionFactory = $connectionFactory; + $this->iteratorFactory = $iteratorFactory; + } + + /** + * Returns custom iterator name for report + * Null for default + * + * @param Query $query + * @return string|null + */ + private function getIteratorName(Query $query) + { + $config = $query->getConfig(); + return isset($config['iterator']) ? $config['iterator'] : null; + } + + /** + * Returns report data by name and criteria + * + * @param string $name + * @param SearchCriteria|null $criteria + * @return \IteratorIterator + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getReport($name, SearchCriteria $criteria = null) + { + $query = $this->queryFactory->create($name); + $connection = $this->connectionFactory->getConnection($query->getConnectionName()); + $statement = $connection->query($query->getSelect()); + return $this->iteratorFactory->create($statement, $this->getIteratorName($query)); + + } +} diff --git a/app/code/Magento/Analytics/ReportXml/SelectHydrator.php b/app/code/Magento/Analytics/ReportXml/SelectHydrator.php new file mode 100644 index 0000000000000..b9a46fd286bd7 --- /dev/null +++ b/app/code/Magento/Analytics/ReportXml/SelectHydrator.php @@ -0,0 +1,96 @@ +resourceConnection = $resourceConnection; + $this->selectParts = $selectParts; + } + + /** + * @return array + */ + private function getSelectParts() + { + return array_merge($this->predefinedSelectParts, $this->selectParts); + } + + /** + * Extracts Select metadata parts + * + * @param Select $select + * @return array + * @throws \Zend_Db_Select_Exception + */ + public function extract(Select $select) + { + $parts = []; + foreach ($this->getSelectParts() as $partName) { + $parts[$partName] = $select->getPart($partName); + } + return $parts; + } + + /** + * @param array $selectParts + * @return Select + */ + public function recreate(array $selectParts) + { + $select = $this->resourceConnection->getConnection()->select(); + foreach ($selectParts as $partName => $partValue) { + $select->setPart($partName, $partValue); + } + return $select; + } +} diff --git a/app/code/Magento/Analytics/Setup/InstallData.php b/app/code/Magento/Analytics/Setup/InstallData.php new file mode 100644 index 0000000000000..b8d311ee5ea15 --- /dev/null +++ b/app/code/Magento/Analytics/Setup/InstallData.php @@ -0,0 +1,43 @@ +notificationTime = $notificationTime; + } + + /** + * {@inheritdoc} + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + $this->notificationTime->storeLastTimeNotification(1); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/BasicTier/SignUpTest.php b/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/BasicTier/SignUpTest.php new file mode 100644 index 0000000000000..c9311ddf1ccc8 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/BasicTier/SignUpTest.php @@ -0,0 +1,84 @@ +configMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultRedirectFactoryMock = $this->getMockBuilder(RedirectFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->redirectMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->signUpController = $this->objectManagerHelper->getObject( + SignUp::class, + [ + 'config' => $this->configMock, + 'resultRedirectFactory' => $this->resultRedirectFactoryMock + ] + ); + } + + /** + * @return void + */ + public function testExecute() + { + $basicTierUrlPath = 'analytics/url/basic_tier'; + $this->configMock->expects($this->once()) + ->method('getConfigDataValue') + ->with($basicTierUrlPath) + ->willReturn('value'); + $this->resultRedirectFactoryMock->expects($this->once())->method('create')->willReturn($this->redirectMock); + $this->redirectMock->expects($this->once())->method('setUrl')->with('value')->willReturnSelf(); + $this->assertEquals($this->redirectMock, $this->signUpController->execute()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Reports/ShowTest.php b/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Reports/ShowTest.php new file mode 100644 index 0000000000000..faedcedd2b5e8 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Reports/ShowTest.php @@ -0,0 +1,155 @@ +reportUrlProviderMock = $this->getMockBuilder(ReportUrlProvider::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resultFactoryMock = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->redirectMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->showController = $this->objectManagerHelper->getObject( + Show::class, + [ + 'reportUrlProvider' => $this->reportUrlProviderMock, + 'resultFactory' => $this->resultFactoryMock, + 'messageManager' => $this->messageManagerMock, + ] + ); + } + + /** + * @return void + */ + public function testExecute() + { + $otpUrl = 'http://example.com?otp=15vbjcfdvd15645'; + + $this->resultFactoryMock + ->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_REDIRECT) + ->willReturn($this->redirectMock); + $this->reportUrlProviderMock + ->expects($this->once()) + ->method('getUrl') + ->with() + ->willReturn($otpUrl); + $this->redirectMock + ->expects($this->once()) + ->method('setUrl') + ->with($otpUrl) + ->willReturnSelf(); + $this->assertSame($this->redirectMock, $this->showController->execute()); + } + + /** + * @dataProvider executeWithExceptionDataProvider + * + * @param \Exception $exception + */ + public function testExecuteWithException(\Exception $exception) + { + + $this->resultFactoryMock + ->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_REDIRECT) + ->willReturn($this->redirectMock); + $this->reportUrlProviderMock + ->expects($this->once()) + ->method('getUrl') + ->with() + ->willThrowException($exception); + if ($exception instanceof LocalizedException) { + $message = $exception->getMessage(); + } else { + $message = __('Sorry, there has been an error processing your request. Please try again later.'); + } + $this->messageManagerMock + ->expects($this->once()) + ->method('addExceptionMessage') + ->with($exception, $message) + ->willReturnSelf(); + $this->redirectMock + ->expects($this->once()) + ->method('setPath') + ->with('adminhtml') + ->willReturnSelf(); + $this->assertSame($this->redirectMock, $this->showController->execute()); + } + + /** + * @return array + */ + public function executeWithExceptionDataProvider() + { + return [ + 'ExecuteWithLocalizedException' => [new LocalizedException(__('TestMessage'))], + 'ExecuteWithException' => [new \Exception('TestMessage')], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Subscription/ActivateTest.php b/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Subscription/ActivateTest.php new file mode 100644 index 0000000000000..a1cf41ace802c --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Subscription/ActivateTest.php @@ -0,0 +1,229 @@ +resultFactoryMock = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resultJsonMock = $this->getMockBuilder(Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->subscriptionModelMock = $this->getMockBuilder(Subscription::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->requestMock = $this->getMockBuilder(RequestInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->notificationTimeMock = $this->getMockBuilder(NotificationTime::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->activateController = $this->objectManagerHelper->getObject( + Activate::class, + [ + 'resultFactory' => $this->resultFactoryMock, + 'subscription' => $this->subscriptionModelMock, + 'logger' => $this->loggerMock, + 'notificationTime' => $this->notificationTimeMock, + '_request' => $this->requestMock, + 'subscriptionApprovedFiled' => $this->subscriptionApprovedField, + ] + ); + } + + /** + * @dataProvider executeDataProvider + * + * @param bool $isSubscriptionEnabled + * @return void + */ + public function testExecute($isSubscriptionEnabled) + { + $successResult = [ + 'success' => true, + 'error_message' => '', + ]; + + $this->requestMock + ->expects($this->once()) + ->method('getParam') + ->with($this->subscriptionApprovedField) + ->willReturn($isSubscriptionEnabled); + + if ($isSubscriptionEnabled) { + $this->subscriptionModelMock + ->expects($this->once()) + ->method('enable') + ->willReturn(true); + } else { + $this->notificationTimeMock + ->expects($this->once()) + ->method('unsetLastTimeNotificationValue') + ->willReturn(true); + } + + $this->resultFactoryMock->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_JSON) + ->willReturn($this->resultJsonMock); + $this->resultJsonMock->expects($this->once()) + ->method('setData') + ->with($successResult) + ->willReturnSelf(); + $this->assertSame( + $this->resultJsonMock, + $this->activateController->execute() + ); + } + + /** + * @dataProvider executeExceptionsDataProvider + * + * @param \Exception $exception + */ + public function testExecuteWithException(\Exception $exception) + { + $this->requestMock + ->expects($this->once()) + ->method('getParam') + ->with($this->subscriptionApprovedField) + ->willReturn(true); + + $this->subscriptionModelMock + ->expects($this->once()) + ->method('enable') + ->willThrowException($exception); + $this->loggerMock + ->expects($this->once()) + ->method('error') + ->with($exception->getMessage()); + $this->resultFactoryMock + ->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_JSON) + ->willReturn($this->resultJsonMock); + + if ($exception instanceof LocalizedException) { + $this->resultJsonMock + ->expects($this->once()) + ->method('setData') + ->with([ + 'success' => false, + 'error_message' => $exception->getMessage(), + ]) + ->willReturnSelf(); + } else { + $this->resultJsonMock + ->expects($this->once()) + ->method('setData') + ->withAnyParameters() + ->willReturnSelf(); + } + + $this->assertSame( + $this->resultJsonMock, + $this->activateController->execute() + ); + } + + /** + * @return array + */ + public function executeExceptionsDataProvider() + { + return [ + [new LocalizedException(__('TestMessage'))], + [new \Exception('TestMessage')], + ]; + } + + /** + * @return array + */ + public function executeDataProvider() + { + return [ + [true], + [false], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Subscription/PostponeTest.php b/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Subscription/PostponeTest.php new file mode 100644 index 0000000000000..78027298028d1 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Controller/Adminhtml/Subscription/PostponeTest.php @@ -0,0 +1,168 @@ +dateTimeFactoryMock = $this->getMockBuilder(DateTimeFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->dateTimeMock = $this->getMockBuilder(\DateTime::class) + ->disableOriginalConstructor() + ->getMock(); + $this->notificationTimeMock = $this->getMockBuilder(NotificationTime::class) + ->disableOriginalConstructor() + ->getMock(); + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) + ->getMock(); + $this->resultFactoryMock = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultMock = $this->getMockBuilder(Json::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultFactoryMock->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_JSON) + ->willReturn($this->resultMock); + $objectManagerHelper = new ObjectManagerHelper($this); + $this->action = $objectManagerHelper->getObject( + Postpone::class, + [ + 'resultFactory' => $this->resultFactoryMock, + 'dateTimeFactory' => $this->dateTimeFactoryMock, + 'notificationTime' => $this->notificationTimeMock, + 'logger' => $this->loggerMock + ] + ); + } + + public function testExecuteSuccess() + { + $this->dateTimeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->dateTimeMock); + $this->dateTimeMock->expects($this->once()) + ->method('getTimestamp') + ->willReturn(100500); + $this->notificationTimeMock->expects($this->once()) + ->method('storeLastTimeNotification') + ->with(100500) + ->willReturn(true); + $this->resultMock->expects($this->once()) + ->method('setData') + ->with( + [ + 'success' => true, + 'error_message' => '' + ], + false, + [] + )->willReturnSelf(); + $this->assertEquals($this->resultMock, $this->action->execute()); + } + + public function testExecuteFailedWithLocalizedException() + { + $this->dateTimeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->dateTimeMock); + $this->dateTimeMock->expects($this->once()) + ->method('getTimestamp') + ->willReturn(100500); + $this->notificationTimeMock->expects($this->once()) + ->method('storeLastTimeNotification') + ->with(100500) + ->willThrowException(new LocalizedException(__('Error message'))); + $this->resultMock->expects($this->once()) + ->method('setData') + ->with( + [ + 'success' => false, + 'error_message' => 'Error message' + ], + false, + [] + )->willReturnSelf(); + $this->assertEquals($this->resultMock, $this->action->execute()); + } + + public function testExecuteFailedWithException() + { + $this->dateTimeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->dateTimeMock); + $this->dateTimeMock->expects($this->once()) + ->method('getTimestamp') + ->willReturn(100500); + $this->notificationTimeMock->expects($this->once()) + ->method('storeLastTimeNotification') + ->with(100500) + ->willThrowException(new \Exception('Any message')); + $this->resultMock->expects($this->once()) + ->method('setData') + ->with( + [ + 'success' => false, + 'error_message' => __('Error occurred during postponement notification') + ], + false, + [] + )->willReturnSelf(); + $this->assertEquals($this->resultMock, $this->action->execute()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Cron/CollectDataTest.php b/app/code/Magento/Analytics/Test/Unit/Cron/CollectDataTest.php new file mode 100644 index 0000000000000..1b65a14659c86 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Cron/CollectDataTest.php @@ -0,0 +1,92 @@ +exportDataHandlerMock = $this->getMockBuilder(ExportDataHandler::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->subscriptionStatusMock = $this->getMockBuilder(SubscriptionStatusProvider::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->collectData = $this->objectManagerHelper->getObject( + CollectData::class, + [ + 'exportDataHandler' => $this->exportDataHandlerMock, + 'subscriptionStatus' => $this->subscriptionStatusMock, + ] + ); + } + + /** + * @param string $status + * @return void + * @dataProvider executeDataProvider + */ + public function testExecute($status) + { + $this->subscriptionStatusMock + ->expects($this->once()) + ->method('getStatus') + ->with() + ->willReturn($status); + $this->exportDataHandlerMock + ->expects(($status === SubscriptionStatusProvider::ENABLED) ? $this->once() : $this->never()) + ->method('prepareExportData') + ->with(); + + $this->assertTrue($this->collectData->execute()); + } + + /** + * @return array + */ + public function executeDataProvider() + { + return [ + 'Subscription is enabled' => [SubscriptionStatusProvider::ENABLED], + 'Subscription is disabled' => [SubscriptionStatusProvider::DISABLED], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Cron/SignUpTest.php b/app/code/Magento/Analytics/Test/Unit/Cron/SignUpTest.php new file mode 100644 index 0000000000000..1b5d0d6a92b1c --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Cron/SignUpTest.php @@ -0,0 +1,171 @@ +connectorMock = $this->getMockBuilder(Connector::class) + ->disableOriginalConstructor() + ->getMock(); + $this->configWriterMock = $this->getMockBuilder(WriterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->inboxFactoryMock = $this->getMockBuilder(InboxFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->inboxResourceMock = $this->getMockBuilder(InboxResource::class) + ->disableOriginalConstructor() + ->getMock(); + $this->flagManagerMock = $this->getMockBuilder(FlagManager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->inboxMock = $this->getMockBuilder(Inbox::class) + ->disableOriginalConstructor() + ->getMock(); + $this->reinitableConfigMock = $this->getMockBuilder(ReinitableConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->signUp = new SignUp( + $this->connectorMock, + $this->configWriterMock, + $this->inboxFactoryMock, + $this->inboxResourceMock, + $this->flagManagerMock, + $this->reinitableConfigMock + ); + } + + public function testExecute() + { + $attemptsCount = 10; + + $this->flagManagerMock->expects($this->once()) + ->method('getFlagData') + ->with(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE) + ->willReturn($attemptsCount); + + $attemptsCount -= 1; + $this->flagManagerMock->expects($this->once()) + ->method('saveFlag') + ->with(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, $attemptsCount); + $this->connectorMock->expects($this->once()) + ->method('execute') + ->with('signUp') + ->willReturn(true); + $this->addDeleteAnalyticsCronExprAsserts(); + $this->flagManagerMock->expects($this->once()) + ->method('deleteFlag') + ->with(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); + $this->assertTrue($this->signUp->execute()); + } + + public function testExecuteFlagNotExist() + { + $this->flagManagerMock->expects($this->once()) + ->method('getFlagData') + ->with(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE) + ->willReturn(null); + $this->addDeleteAnalyticsCronExprAsserts(); + $this->assertFalse($this->signUp->execute()); + } + + public function testExecuteZeroAttempts() + { + $attemptsCount = 0; + $this->flagManagerMock->expects($this->once()) + ->method('getFlagData') + ->with(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE) + ->willReturn($attemptsCount); + $this->addDeleteAnalyticsCronExprAsserts(); + $this->flagManagerMock->expects($this->once()) + ->method('deleteFlag') + ->with(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE); + $this->inboxFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->inboxMock); + $this->inboxMock->expects($this->once()) + ->method('addNotice'); + $this->inboxResourceMock->expects($this->once()) + ->method('save') + ->with($this->inboxMock); + $this->assertFalse($this->signUp->execute()); + } + + /** + * Add assertions for method deleteAnalyticsCronExpr. + * + * @return void + */ + private function addDeleteAnalyticsCronExprAsserts() + { + $this->configWriterMock + ->expects($this->once()) + ->method('delete') + ->with(SubscriptionHandler::CRON_STRING_PATH) + ->willReturn(true); + $this->reinitableConfigMock + ->expects($this->once()) + ->method('reinit') + ->willReturnSelf(); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php b/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php new file mode 100644 index 0000000000000..ba52571710212 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Cron/UpdateTest.php @@ -0,0 +1,93 @@ +connectorMock = $this->getMockBuilder(Connector::class) + ->disableOriginalConstructor() + ->getMock(); + $this->configWriterMock = $this->getMockBuilder(WriterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->flagManagerMock = $this->getMockBuilder(FlagManager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->reinitableConfigMock = $this->getMockBuilder(ReinitableConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->update = new Update( + $this->connectorMock, + $this->configWriterMock, + $this->reinitableConfigMock, + $this->flagManagerMock + ); + } + + public function testExecute() + { + $this->connectorMock->expects($this->once()) + ->method('execute') + ->with('update') + ->willReturn(true); + $this->configWriterMock->expects($this->once()) + ->method('delete') + ->with(BaseUrlConfigPlugin::UPDATE_CRON_STRING_PATH); + $this->flagManagerMock->expects($this->once()) + ->method('deleteFlag') + ->with(BaseUrlConfigPlugin::OLD_BASE_URL_FLAG_CODE); + $this->reinitableConfigMock->expects($this->once()) + ->method('reinit'); + $this->assertTrue($this->update->execute()); + } + + public function testExecuteUnsuccess() + { + $this->connectorMock->expects($this->once()) + ->method('execute') + ->with('update') + ->willReturn(false); + $this->assertFalse($this->update->execute()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/AnalyticsTokenTest.php b/app/code/Magento/Analytics/Test/Unit/Model/AnalyticsTokenTest.php new file mode 100644 index 0000000000000..2409ec7c110ca --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/AnalyticsTokenTest.php @@ -0,0 +1,129 @@ +reinitableConfigMock = $this->getMockBuilder(ReinitableConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configWriterMock = $this->getMockBuilder(WriterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->tokenModel = $this->objectManagerHelper->getObject( + AnalyticsToken::class, + [ + 'reinitableConfig' => $this->reinitableConfigMock, + 'config' => $this->configMock, + 'configWriter' => $this->configWriterMock, + 'tokenPath' => $this->tokenPath, + ] + ); + } + + /** + * @return void + */ + public function testStoreToken() + { + $value = 'jjjj0000'; + + $this->configWriterMock + ->expects($this->once()) + ->method('save') + ->with($this->tokenPath, $value); + + $this->reinitableConfigMock + ->expects($this->once()) + ->method('reinit') + ->willReturnSelf(); + + $this->assertTrue($this->tokenModel->storeToken($value)); + } + + /** + * @return void + */ + public function testGetToken() + { + $value = 'jjjj0000'; + + $this->configMock + ->expects($this->once()) + ->method('getValue') + ->with($this->tokenPath) + ->willReturn($value); + + $this->assertSame($value, $this->tokenModel->getToken()); + } + + /** + * @return void + */ + public function testIsTokenExist() + { + $this->assertFalse($this->tokenModel->isTokenExist()); + + $this->configMock + ->expects($this->once()) + ->method('getValue') + ->with($this->tokenPath) + ->willReturn('0000'); + $this->assertTrue($this->tokenModel->isTokenExist()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Condition/CanViewNotificationTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Condition/CanViewNotificationTest.php new file mode 100644 index 0000000000000..f361fde03f02d --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Condition/CanViewNotificationTest.php @@ -0,0 +1,81 @@ +dateTimeFactoryMock = $this->getMockBuilder(DateTimeFactory::class) + ->getMock(); + $this->dateTimeMock = $this->getMockBuilder(\DateTime::class) + ->getMock(); + $this->notificationTimeMock = $this->getMockBuilder(NotificationTime::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManager = new ObjectManager($this); + $this->canViewNotification = $objectManager->getObject( + CanViewNotification::class, + [ + 'notificationTime' => $this->notificationTimeMock, + 'dateTimeFactory' => $this->dateTimeFactoryMock + ] + ); + } + + public function testValidate() + { + $this->notificationTimeMock->expects($this->once()) + ->method('getLastTimeNotification') + ->willReturn(1); + $this->dateTimeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->dateTimeMock); + $this->dateTimeMock->expects($this->once()) + ->method('getTimestamp') + ->willReturn(10005000); + $this->assertTrue($this->canViewNotification->validate()); + } + + public function testValidateFlagRemoved() + { + $this->notificationTimeMock->expects($this->once()) + ->method('getLastTimeNotification') + ->willReturn(null); + $this->dateTimeFactoryMock->expects($this->never()) + ->method('create'); + $this->assertFalse($this->canViewNotification->validate()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/CollectionTimeTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/CollectionTimeTest.php new file mode 100644 index 0000000000000..132aa4aac9529 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/CollectionTimeTest.php @@ -0,0 +1,112 @@ +configWriterMock = $this->getMockBuilder(WriterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->collectionTime = $this->objectManagerHelper->getObject( + CollectionTime::class, + [ + 'configWriter' => $this->configWriterMock, + '_logger' => $this->loggerMock, + ] + ); + } + + /** + * @return void + */ + public function testAfterSave() + { + $this->collectionTime->setData('value', '05,04,03'); + + $this->configWriterMock + ->expects($this->once()) + ->method('save') + ->with(CollectionTime::CRON_SCHEDULE_PATH, join(' ', ['04', '05', '*', '*', '*'])); + + $this->assertInstanceOf( + Value::class, + $this->collectionTime->afterSave() + ); + + } + + /** + * @return void + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testAfterSaveWrongValue() + { + $this->collectionTime->setData('value', '00,01'); + $this->collectionTime->afterSave(); + } + + /** + * @return void + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testAfterSaveWithLocalizedException() + { + $exception = new \Exception('Test message'); + $this->collectionTime->setData('value', '05,04,03'); + + $this->configWriterMock + ->expects($this->once()) + ->method('save') + ->with(CollectionTime::CRON_SCHEDULE_PATH, join(' ', ['04', '05', '*', '*', '*'])) + ->willThrowException($exception); + $this->loggerMock + ->expects($this->once()) + ->method('error') + ->with($exception->getMessage()); + $this->collectionTime->afterSave(); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/Enabled/SubscriptionHandlerTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/Enabled/SubscriptionHandlerTest.php new file mode 100644 index 0000000000000..f7a299cd4c657 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/Enabled/SubscriptionHandlerTest.php @@ -0,0 +1,187 @@ +flagManagerMock = $this->getMockBuilder(FlagManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configWriterMock = $this->getMockBuilder(WriterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->tokenMock = $this->getMockBuilder(AnalyticsToken::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->notificationTimeMock = $this->getMockBuilder(NotificationTime::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configValueMock = $this->getMockBuilder(Value::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->subscriptionHandler = $this->objectManagerHelper->getObject( + SubscriptionHandler::class, + [ + 'flagManager' => $this->flagManagerMock, + 'configWriter' => $this->configWriterMock, + 'attemptsInitValue' => $this->attemptsInitValue, + 'analyticsToken' => $this->tokenMock, + 'notificationTime' => $this->notificationTimeMock, + ] + ); + } + + /** + * @param int|null $value null means that $value was not changed + * @param bool $isTokenExist + * + * @dataProvider processDataProvider + */ + public function testProcess($value, $isTokenExist) + { + $this->configValueMock + ->expects($this->once()) + ->method('isValueChanged') + ->willReturn(is_int($value)); + $this->configValueMock + ->expects(is_int($value) ? $this->once() : $this->never()) + ->method('getData') + ->with('value') + ->willReturn($value); + $this->configWriterMock + ->expects(($value === 0) ? $this->once() : $this->never()) + ->method('delete') + ->with(CollectionTime::CRON_SCHEDULE_PATH); + $this->tokenMock + ->expects(is_int($value) ? $this->once() : $this->never()) + ->method('isTokenExist') + ->willReturn($isTokenExist); + if (is_int($value) && !$isTokenExist) { + if ($value === 1) { + $this->addProcessWithEnabledTrueAsserts(); + } elseif ($value === 0) { + $this->addProcessWithEnabledFalseAsserts(); + } + } + $this->assertTrue( + $this->subscriptionHandler->process($this->configValueMock) + ); + } + + /** + * Add assertions for method process in case when new config value equals 1. + * + * @return void + */ + private function addProcessWithEnabledTrueAsserts() + { + $this->configWriterMock + ->expects($this->once()) + ->with(SubscriptionHandler::CRON_STRING_PATH, "0 * * * *") + ->method('save'); + $this->flagManagerMock + ->expects($this->once()) + ->method('saveFlag') + ->with(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, $this->attemptsInitValue) + ->willReturn(true); + $this->notificationTimeMock + ->expects($this->once()) + ->method('unsetLastTimeNotificationValue') + ->willReturn(true); + } + + /** + * Add assertions for method process in case when new config value equals 0. + * + * @return void + */ + private function addProcessWithEnabledFalseAsserts() + { + $this->flagManagerMock + ->expects($this->once()) + ->method('deleteFlag') + ->with(SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE) + ->willReturn(true); + } + + /** + * Data provider for process test. + * + * @return array + */ + public function processDataProvider() + { + return [ + 'Config value has not changed and token exist' => [null, true], + 'Config value has not changed and token doesn\'t exist' => [null, false], + 'Config value is "No" and token exist' => [0, true], + 'Config value is "Yes" and token exist' => [1, true], + 'Config value is "No" and token doesn\'t exist' => [0, false], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/EnabledTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/EnabledTest.php new file mode 100644 index 0000000000000..336c80217b4c6 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/EnabledTest.php @@ -0,0 +1,99 @@ +subscriptionHandlerMock = $this->getMockBuilder(SubscriptionHandler::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->enabledModel = $this->objectManagerHelper->getObject( + Enabled::class, + [ + 'subscriptionHandler' => $this->subscriptionHandlerMock, + '_logger' => $this->loggerMock, + ] + ); + } + + /** + * @return void + */ + public function testAfterSaveSuccess() + { + $this->subscriptionHandlerMock + ->expects($this->once()) + ->method('process') + ->with($this->enabledModel) + ->willReturn(true); + + $this->assertInstanceOf( + Value::class, + $this->enabledModel->afterSave() + ); + } + + /** + * @return void + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testExecuteAfterSaveFailedWithLocalizedException() + { + $exception = new \Exception('Message'); + + $this->subscriptionHandlerMock + ->expects($this->once()) + ->method('process') + ->with($this->enabledModel) + ->willThrowException($exception); + + $this->loggerMock + ->expects($this->once()) + ->method('error') + ->with($exception->getMessage()); + + $this->enabledModel->afterSave(); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/VerticalTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/VerticalTest.php new file mode 100644 index 0000000000000..72c70e3a85398 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Config/Backend/VerticalTest.php @@ -0,0 +1,59 @@ +objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\Model\Config\Backend\Vertical::class + ); + } + + /** + * @return void + */ + public function testBeforeSaveSuccess() + { + $this->subject->setValue('Apps and Games'); + + $this->assertInstanceOf( + \Magento\Analytics\Model\Config\Backend\Vertical::class, + $this->subject->beforeSave() + ); + } + + /** + * @return void + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testBeforeSaveFailedWithLocalizedException() + { + $this->subject->setValue(''); + + $this->subject->beforeSave(); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Config/MapperTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Config/MapperTest.php new file mode 100644 index 0000000000000..d39ada40f811e --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Config/MapperTest.php @@ -0,0 +1,142 @@ +objectManagerHelper = new ObjectManagerHelper($this); + + $this->mapper = $this->objectManagerHelper->getObject(Mapper::class); + } + + /** + * @param array $configData + * @param array $resultData + * @return void + * + * @dataProvider executingDataProvider + */ + public function testExecution($configData, $resultData) + { + $this->assertSame($resultData, $this->mapper->execute($configData)); + } + + /** + * @return array + */ + public function executingDataProvider() + { + return [ + 'wrongConfig' => [ + ['config' => ['files']], + [] + ], + 'validConfigWithFileNodes' => [ + [ + 'config' => [ + 0 => [ + 'file' => [ + 0 => [ + 'name' => 'fileName', + 'providers' => [[]] + ] + ] + ] + ] + ], + [ + 'fileName' => [ + 'name' => 'fileName', + 'providers' => [] + ] + ], + ], + 'validConfigWithProvidersNode' => [ + [ + 'config' => [ + 0 => [ + 'file' => [ + 0 => [ + 'name' => 'fileName', + 'providers' => [ + 0 => [ + 'reportProvider' => [0 => []] + ] + ] + ] + ] + ] + ] + ], + [ + 'fileName' => [ + 'name' => 'fileName', + 'providers' => [ + 'reportProvider' => ['parameters' => []] + ] + ] + ], + ], + 'validConfigWithParametersNode' => [ + [ + 'config' => [ + 0 => [ + 'file' => [ + 0 => [ + 'name' => 'fileName', + 'providers' => [ + 0 => [ + 'reportProvider' => [ + 0 => [ + 'parameters' => [ + 0 => ['name' => ['reportName']] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ], + [ + 'fileName' => [ + 'name' => 'fileName', + 'providers' => [ + 'reportProvider' => [ + 'parameters' => [ + 'name' => 'reportName' + ] + ] + ] + ] + ], + ], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Config/ReaderTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Config/ReaderTest.php new file mode 100644 index 0000000000000..41883e2c8e3ad --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Config/ReaderTest.php @@ -0,0 +1,108 @@ +mapperMock = $this->getMockBuilder(Mapper::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->readerXmlMock = $this->getMockBuilder(ReaderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->readerDbMock = $this->getMockBuilder(ReaderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->reader = $this->objectManagerHelper->getObject( + Reader::class, + [ + 'mapper' => $this->mapperMock, + 'readers' => [ + $this->readerXmlMock, + $this->readerDbMock, + ], + ] + ); + } + + /** + * @return void + */ + public function testRead() + { + $scope = 'store'; + $xmlReaderResult = [ + 'config' => ['node1' => ['node2' => 'node4']] + ]; + $dbReaderResult = [ + 'config' => ['node1' => ['node2' => 'node3']] + ]; + $mapperResult = ['node2' => ['node3', 'node4']]; + + $this->readerXmlMock + ->expects($this->once()) + ->method('read') + ->with($scope) + ->willReturn($xmlReaderResult); + + $this->readerDbMock + ->expects($this->once()) + ->method('read') + ->with($scope) + ->willReturn($dbReaderResult); + + $this->mapperMock + ->expects($this->once()) + ->method('execute') + ->with(array_merge_recursive($xmlReaderResult, $dbReaderResult)) + ->willReturn($mapperResult); + + $this->assertSame($mapperResult, $this->reader->read($scope)); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Config/SchemaLocatorTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Config/SchemaLocatorTest.php new file mode 100644 index 0000000000000..e486268e06bb2 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Config/SchemaLocatorTest.php @@ -0,0 +1,80 @@ +urnResolverMock = $this->getMockBuilder(UrnResolver::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->schemaLocator = $this->objectManagerHelper->getObject( + SchemaLocator::class, + [ + 'urnResolver' => $this->urnResolverMock, + 'schema' => $this->schema, + ] + ); + } + + /** + * @return void + */ + public function testGetSchema() + { + $schemaRealPath = '/path/test.xml'; + + $this->urnResolverMock + ->expects($this->once()) + ->method('getRealPath') + ->with($this->schema) + ->willReturn($schemaRealPath); + + $this->assertSame($schemaRealPath, $this->schemaLocator->getSchema()); + } + + /** + * @return void + */ + public function testGetPerFileSchema() + { + $this->assertNull($this->schemaLocator->getPerFileSchema()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Config/Source/VerticalTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Config/Source/VerticalTest.php new file mode 100644 index 0000000000000..0213e61d9ab35 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Config/Source/VerticalTest.php @@ -0,0 +1,60 @@ +objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\Model\Config\Source\Vertical::class, + [ + 'verticals' => [ + 'Apps and Games', + 'Athletic/Sporting Goods', + 'Art and Design' + ] + ] + ); + } + + /** + * @return void + */ + public function testToOptionArray() + { + $expectedOptionsArray = [ + ['value' => '', 'label' => __('--Please Select--')], + ['value' => 'Apps and Games', 'label' => __('Apps and Games')], + ['value' => 'Athletic/Sporting Goods', 'label' => __('Athletic/Sporting Goods')], + ['value' => 'Art and Design', 'label' => __('Art and Design')] + ]; + + $this->assertEquals( + $expectedOptionsArray, + $this->subject->toOptionArray() + ); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Analytics/Test/Unit/Model/ConfigTest.php new file mode 100644 index 0000000000000..0876a9dba3db6 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/ConfigTest.php @@ -0,0 +1,68 @@ +dataInterfaceMock = $this->getMockBuilder(DataInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->config = $this->objectManagerHelper->getObject( + Config::class, + [ + 'data' => $this->dataInterfaceMock, + ] + ); + } + + /** + * @return void + */ + public function testGet() + { + $key = 'configKey'; + $defaultValue = 'mock'; + $configValue = 'emptyString'; + + $this->dataInterfaceMock + ->expects($this->once()) + ->method('get') + ->with($key, $defaultValue) + ->willReturn($configValue); + + $this->assertSame($configValue, $this->config->get($key, $defaultValue)); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/Client/CurlTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/Client/CurlTest.php new file mode 100644 index 0000000000000..20f7e80bf0287 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/Http/Client/CurlTest.php @@ -0,0 +1,203 @@ +curlMock = $this->getMockBuilder( + \Magento\Framework\HTTP\Adapter\Curl::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->loggerMock = $this->getMockBuilder( + \Psr\Log\LoggerInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->responseMock = $this->getMockBuilder( + \Zend_Http_Response::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->curlFactoryMock = $this->getMockBuilder(CurlFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->curlFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->curlMock); + + $this->responseFactoryMock = $this->getMockBuilder( + \Magento\Analytics\Model\Connector\Http\ResponseFactory::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\Model\Connector\Http\Client\Curl::class, + [ + 'curlFactory' => $this->curlFactoryMock, + 'responseFactory' => $this->responseFactoryMock, + 'logger' => $this->loggerMock + ] + ); + } + + /** + * Returns test parameters for request. + * + * @return array + */ + public function getTestData() + { + return [ + [ + 'data' => [ + 'version' => '1.1', + 'body'=> '{"name": "value"}', + 'url' => 'http://www.mystore.com', + 'headers' => ['Content-Type: application/json'], + 'method' => \Magento\Framework\HTTP\ZendClient::POST, + ] + ] + ]; + } + + /** + * @return void + * @dataProvider getTestData + */ + public function testRequestSuccess(array $data) + { + $responseString = 'This is response.'; + + $this->curlMock->expects($this->once()) + ->method('write') + ->with( + $data['method'], + $data['url'], + $data['version'], + $data['headers'], + $data['version'] + ); + $this->curlMock->expects($this->once()) + ->method('read') + ->willReturn($responseString); + $this->curlMock->expects($this->any()) + ->method('getErrno') + ->willReturn(0); + + $this->responseFactoryMock->expects($this->any()) + ->method('create') + ->with($responseString) + ->willReturn($this->responseMock); + + $this->assertEquals( + $this->responseMock, + $this->subject->request( + $data['method'], + $data['url'], + $data['version'], + $data['headers'], + $data['version'] + ) + ); + } + + /** + * @return void + * @dataProvider getTestData + */ + public function testRequestError(array $data) + { + $this->curlMock->expects($this->once()) + ->method('write') + ->with( + $data['method'], + $data['url'], + $data['version'], + $data['headers'], + $data['version'] + ); + $this->curlMock->expects($this->once()) + ->method('read'); + $this->curlMock->expects($this->atLeastOnce()) + ->method('getErrno') + ->willReturn(1); + $this->curlMock->expects($this->atLeastOnce()) + ->method('getError') + ->willReturn('CURL error.'); + + $this->loggerMock->expects($this->once()) + ->method('critical') + ->with( + new \Exception( + 'MBI service CURL connection error #1: CURL error.' + ) + ); + + $this->assertFalse( + $this->subject->request( + $data['method'], + $data['url'], + $data['version'], + $data['headers'], + $data['version'] + ) + ); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/OTPRequestTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/OTPRequestTest.php new file mode 100644 index 0000000000000..f8faef12adc77 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/OTPRequestTest.php @@ -0,0 +1,275 @@ +loggerMock = $this->getMockBuilder( + \Psr\Log\LoggerInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->configMock = $this->getMockBuilder( + \Magento\Framework\App\Config\ScopeConfigInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->responseMock = $this->getMockBuilder( + \Zend_Http_Response::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->httpClientMock = $this->getMockBuilder( + \Magento\Analytics\Model\Connector\Http\ClientInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->analyticsTokenMock = $this->getMockBuilder( + \Magento\Analytics\Model\AnalyticsToken::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\Model\Connector\OTPRequest::class, + [ + 'analyticsToken' => $this->analyticsTokenMock, + 'config' => $this->configMock, + 'httpClient' => $this->httpClientMock, + 'logger' => $this->loggerMock + ] + ); + } + + /** + * Returns test parameters for request. + * + * @return array + */ + private function getTestData() + { + return [ + 'otp' => 'thisisotp', + 'url' => 'http://www.mystore.com', + 'access-token' => 'thisisaccesstoken', + 'headers' => ['Content-Type: application/json'], + 'method' => \Magento\Framework\HTTP\ZendClient::POST, + 'body'=> '{"access-token":"thisisaccesstoken","url":"http:\/\/www.mystore.com"}', + ]; + } + + /** + * @return void + */ + public function testCallSuccess() + { + $data = $this->getTestData(); + + $this->analyticsTokenMock->expects($this->once()) + ->method('isTokenExist') + ->willReturn(true); + $this->analyticsTokenMock->expects($this->once()) + ->method('getToken') + ->willReturn($data['access-token']); + + $this->configMock->expects($this->any()) + ->method('getValue') + ->willReturn($data['url']); + + $this->httpClientMock->expects($this->once()) + ->method('request') + ->with( + $data['method'], + $data['url'], + $data['body'], + $data['headers'] + ) + ->willReturn($this->responseMock); + + $this->responseMock->expects($this->any()) + ->method('getStatus') + ->willReturn(201); + $this->responseMock->expects($this->any()) + ->method('getBody') + ->willReturn('{"otp": "' . $data['otp'] . '"}'); + + $this->assertEquals( + $data['otp'], + $this->subject->call() + ); + } + + /** + * @return void + */ + public function testCallNoAccessToken() + { + $this->analyticsTokenMock->expects($this->once()) + ->method('isTokenExist') + ->willReturn(false); + + $this->httpClientMock->expects($this->never()) + ->method('request'); + + $this->assertFalse($this->subject->call()); + } + + /** + * @return void + */ + public function testCallTransportFailure() + { + $data = $this->getTestData(); + + $this->analyticsTokenMock->expects($this->once()) + ->method('isTokenExist') + ->willReturn(true); + $this->analyticsTokenMock->expects($this->once()) + ->method('getToken') + ->willReturn($data['access-token']); + + $this->configMock->expects($this->any()) + ->method('getValue') + ->willReturn($data['url']); + + $this->httpClientMock->expects($this->once()) + ->method('request') + ->with( + $data['method'], + $data['url'], + $data['body'], + $data['headers'] + ) + ->willReturn(false); + + $this->assertFalse($this->subject->call()); + } + + /** + * @return void + */ + public function testCallNoOtp() + { + $data = $this->getTestData(); + + $this->analyticsTokenMock->expects($this->once()) + ->method('isTokenExist') + ->willReturn(true); + $this->analyticsTokenMock->expects($this->once()) + ->method('getToken') + ->willReturn($data['access-token']); + + $this->configMock->expects($this->any()) + ->method('getValue') + ->willReturn($data['url']); + + $this->httpClientMock->expects($this->once()) + ->method('request') + ->with( + $data['method'], + $data['url'], + $data['body'], + $data['headers'] + ) + ->willReturn($this->responseMock); + + $this->responseMock->expects($this->any()) + ->method('getStatus') + ->willReturn(409); + + $this->loggerMock->expects($this->once()) + ->method('warning'); + + $this->assertFalse($this->subject->call()); + } + + /** + * @return void + */ + public function testCallException() + { + $data = $this->getTestData(); + + $exception = new \Exception('Test Exception'); + + $this->analyticsTokenMock->expects($this->once()) + ->method('isTokenExist') + ->willReturn(true); + $this->analyticsTokenMock->expects($this->once()) + ->method('getToken') + ->willReturn($data['access-token']); + + $this->configMock->expects($this->any()) + ->method('getValue') + ->willReturn($data['url']); + + $this->httpClientMock->expects($this->once()) + ->method('request') + ->with( + $data['method'], + $data['url'], + $data['body'], + $data['headers'] + ) + ->willThrowException($exception); + + $this->loggerMock->expects($this->once()) + ->method('critical') + ->with($exception); + + $this->assertFalse($this->subject->call()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php new file mode 100644 index 0000000000000..42d7ea0824ad5 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpCommandTest.php @@ -0,0 +1,126 @@ +analyticsTokenMock = $this->getMockBuilder(AnalyticsToken::class) + ->disableOriginalConstructor() + ->getMock(); + $this->integrationManagerMock = $this->getMockBuilder(IntegrationManager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->integrationToken = $this->getMockBuilder(IntegrationToken::class) + ->disableOriginalConstructor() + ->setMethods(['getToken']) + ->getMock(); + $this->integrationToken->expects($this->any()) + ->method('getToken') + ->willReturn('IntegrationToken'); + $this->signUpRequestMock = $this->getMockBuilder(SignUpRequest::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManagerHelper = new ObjectManagerHelper($this); + $this->signUpCommand = $objectManagerHelper->getObject( + SignUpCommand::class, + [ + 'analyticsToken' => $this->analyticsTokenMock, + 'integrationManager' => $this->integrationManagerMock, + 'signUpRequest' => $this->signUpRequestMock + ] + ); + } + + public function testExecuteSuccess() + { + $this->integrationManagerMock->expects($this->once()) + ->method('generateToken') + ->willReturn($this->integrationToken); + $this->integrationManagerMock->expects($this->once()) + ->method('activateIntegration') + ->willReturn(true); + $this->signUpRequestMock->expects($this->once()) + ->method('call') + ->with('IntegrationToken') + ->willReturn('MAToken'); + $this->analyticsTokenMock->expects($this->once()) + ->method('storeToken') + ->with('MAToken') + ->willReturn(true); + $this->assertTrue($this->signUpCommand->execute()); + } + + public function testExecuteFailureCannotGenerateToken() + { + $this->integrationManagerMock->expects($this->once()) + ->method('generateToken') + ->willReturn(false); + $this->integrationManagerMock->expects($this->never()) + ->method('activateIntegration') + ->willReturn(true); + $this->signUpRequestMock->expects($this->never()) + ->method('call') + ->willReturn('MAToken'); + $this->analyticsTokenMock->expects($this->never()) + ->method('storeToken') + ->willReturn(true); + $this->assertFalse($this->signUpCommand->execute()); + } + + public function testExecuteFailureResponseIsEmpty() + { + $this->integrationManagerMock->expects($this->once()) + ->method('generateToken') + ->willReturn($this->integrationToken); + $this->integrationManagerMock->expects($this->once()) + ->method('activateIntegration') + ->willReturn(true); + $this->signUpRequestMock->expects($this->once()) + ->method('call') + ->with('IntegrationToken') + ->willReturn(false); + $this->analyticsTokenMock->expects($this->never()) + ->method('storeToken'); + $this->assertFalse($this->signUpCommand->execute()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpRequestTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpRequestTest.php new file mode 100644 index 0000000000000..98ea13e092fa6 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/SignUpRequestTest.php @@ -0,0 +1,226 @@ +loggerMock = $this->getMockBuilder( + \Psr\Log\LoggerInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->configMock = $this->getMockBuilder( + \Magento\Config\Model\Config::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->responseMock = $this->getMockBuilder( + \Zend_Http_Response::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->httpClientMock = $this->getMockBuilder( + \Magento\Analytics\Model\Connector\Http\ClientInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\Model\Connector\SignUpRequest::class, + [ + 'config' => $this->configMock, + 'httpClient' => $this->httpClientMock, + 'logger' => $this->loggerMock + ] + ); + } + + /** + * Returns test parameters for request. + * + * @return array + */ + private function getTestData() + { + return [ + 'url' => 'http://www.mystore.com', + 'access-token' => 'thisisaccesstoken', + 'integration-token' => 'thisisintegrationtoken', + 'headers' => ['Content-Type: application/json'], + 'method' => \Magento\Framework\HTTP\ZendClient::POST, + 'body'=> '{"token":"thisisintegrationtoken","url":"http:\/\/www.mystore.com"}', + ]; + } + + /** + * @return void + */ + public function testCallSuccess() + { + $data = $this->getTestData(); + + $this->configMock->expects($this->any()) + ->method('getConfigDataValue') + ->willReturn($data['url']); + + $this->httpClientMock->expects($this->once()) + ->method('request') + ->with( + $data['method'], + $data['url'], + $data['body'], + $data['headers'] + ) + ->willReturn($this->responseMock); + + $this->responseMock->expects($this->any()) + ->method('getStatus') + ->willReturn(201); + $this->responseMock->expects($this->any()) + ->method('getBody') + ->willReturn('{"access-token": "' . $data['access-token'] . '"}'); + + $this->assertEquals( + $data['access-token'], + $this->subject->call($data['integration-token']) + ); + } + + /** + * @return void + */ + public function testCallTransportFailure() + { + $data = $this->getTestData(); + + $this->configMock->expects($this->any()) + ->method('getConfigDataValue') + ->willReturn($data['url']); + + $this->httpClientMock->expects($this->once()) + ->method('request') + ->with( + $data['method'], + $data['url'], + $data['body'], + $data['headers'] + ) + ->willReturn(false); + + $this->assertFalse( + $this->subject->call($data['integration-token']) + ); + } + + /** + * @return void + */ + public function testCallNoAccessToken() + { + $data = $this->getTestData(); + + $this->configMock->expects($this->any()) + ->method('getConfigDataValue') + ->willReturn($data['url']); + + $this->httpClientMock->expects($this->once()) + ->method('request') + ->with( + $data['method'], + $data['url'], + $data['body'], + $data['headers'] + ) + ->willReturn($this->responseMock); + + $this->responseMock->expects($this->any()) + ->method('getStatus') + ->willReturn(409); + + $this->loggerMock->expects($this->once()) + ->method('warning'); + + $this->assertFalse( + $this->subject->call($data['integration-token']) + ); + } + + /** + * @return void + */ + public function testCallException() + { + $data = $this->getTestData(); + + $exception = new \Exception('Test Exception'); + + $this->configMock->expects($this->any()) + ->method('getConfigDataValue') + ->willReturn($data['url']); + + $this->httpClientMock->expects($this->once()) + ->method('request') + ->with( + $data['method'], + $data['url'], + $data['body'], + $data['headers'] + ) + ->willThrowException($exception); + + $this->loggerMock->expects($this->once()) + ->method('critical') + ->with($exception); + + $this->assertFalse( + $this->subject->call($data['integration-token']) + ); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Connector/UpdateCommandTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Connector/UpdateCommandTest.php new file mode 100644 index 0000000000000..f16944d2f3417 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Connector/UpdateCommandTest.php @@ -0,0 +1,139 @@ +analyticsTokenMock = $this->getMockBuilder(AnalyticsToken::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->httpClientMock = $this->getMockBuilder(ClientInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->flagManagerMock = $this->getMockBuilder(FlagManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->responseMock = $this->getMockBuilder(\Zend_Http_Response::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->updateCommand = new UpdateCommand( + $this->analyticsTokenMock, + $this->httpClientMock, + $this->configMock, + $this->loggerMock, + $this->flagManagerMock + ); + } + + public function testExecuteSuccess() + { + $url = "old.localhost.com"; + $configVal = "Config val"; + $token = "Secret token!"; + $requestJson = sprintf('{"url":"%s","new-url":"%s","access-token":"%s"}', $url, $configVal, $token); + $this->analyticsTokenMock->expects($this->once()) + ->method('isTokenExist') + ->willReturn(true); + + $this->configMock->expects($this->any()) + ->method('getConfigDataValue') + ->willReturn($configVal); + + $this->flagManagerMock->expects($this->once()) + ->method('getFlagData') + ->with(BaseUrlConfigPlugin::OLD_BASE_URL_FLAG_CODE) + ->willReturn($url); + + $this->analyticsTokenMock->expects($this->once()) + ->method('getToken') + ->willReturn($token); + + $this->httpClientMock->expects($this->once()) + ->method('request') + ->with( + ZendClient::PUT, + $configVal, + $requestJson, + ['Content-Type: application/json'] + )->willReturn($this->responseMock); + + $this->responseMock->expects($this->once()) + ->method('getStatus') + ->willReturn(201); + + $this->assertTrue($this->updateCommand->execute()); + } + + public function testExecuteWithoutToken() + { + $this->analyticsTokenMock->expects($this->once()) + ->method('isTokenExist') + ->willReturn(false); + + $this->assertFalse($this->updateCommand->execute()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/ConnectorTest.php b/app/code/Magento/Analytics/Test/Unit/Model/ConnectorTest.php new file mode 100644 index 0000000000000..a52186adf989e --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/ConnectorTest.php @@ -0,0 +1,70 @@ +objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->signUpCommandMock = $this->getMockBuilder(SignUpCommand::class) + ->disableOriginalConstructor() + ->getMock(); + $this->commands = ['signUp' => SignUpCommand::class]; + $this->connector = new Connector($this->commands, $this->objectManagerMock); + } + + public function testExecute() + { + $commandName = 'signUp'; + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with($this->commands[$commandName]) + ->willReturn($this->signUpCommandMock); + $this->signUpCommandMock->expects($this->once()) + ->method('execute') + ->willReturn(true); + $this->assertTrue($this->connector->execute($commandName)); + } + + /** + * @expectedException \Magento\Framework\Exception\NotFoundException + */ + public function testExecuteCommandNotFound() + { + $commandName = 'register'; + $this->connector->execute($commandName); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/CryptographerTest.php b/app/code/Magento/Analytics/Test/Unit/Model/CryptographerTest.php new file mode 100644 index 0000000000000..4dccb54e192f5 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/CryptographerTest.php @@ -0,0 +1,226 @@ +analyticsTokenMock = $this->getMockBuilder(AnalyticsToken::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->encodedContextFactoryMock = $this->getMockBuilder(EncodedContextFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $this->encodedContextMock = $this->getMockBuilder(EncodedContext::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->key = ''; + $this->source = ''; + $this->initializationVectors = []; + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->cryptographer = $this->objectManagerHelper->getObject( + Cryptographer::class, + [ + 'analyticsToken' => $this->analyticsTokenMock, + 'encodedContextFactory' => $this->encodedContextFactoryMock, + 'cipherMethod' => $this->cipherMethod, + ] + ); + } + + /** + * @return void + */ + public function testEncode() + { + $token = 'some-token-value'; + $this->source = 'Some text'; + $this->key = hash('sha256', $token); + + $checkEncodedContext = function ($parameters) { + $emptyRequiredParameters = + array_diff(['content', 'initializationVector'], array_keys(array_filter($parameters))); + if ($emptyRequiredParameters) { + return false; + } + + $encryptedData = openssl_encrypt( + $this->source, + $this->cipherMethod, + $this->key, + OPENSSL_RAW_DATA, + $parameters['initializationVector'] + ); + + return ($encryptedData === $parameters['content']); + }; + + $this->analyticsTokenMock + ->expects($this->once()) + ->method('getToken') + ->with() + ->willReturn($token); + + $this->encodedContextFactoryMock + ->expects($this->once()) + ->method('create') + ->with($this->callback($checkEncodedContext)) + ->willReturn($this->encodedContextMock); + + $this->assertSame($this->encodedContextMock, $this->cryptographer->encode($this->source)); + } + + /** + * @return void + */ + public function testEncodeUniqueInitializationVector() + { + $this->source = 'Some text'; + $token = 'some-token-value'; + + $registerInitializationVector = function ($parameters) { + if (empty($parameters['initializationVector'])) { + return false; + } + + $this->initializationVectors[] = $parameters['initializationVector']; + + return true; + }; + + $this->analyticsTokenMock + ->expects($this->exactly(2)) + ->method('getToken') + ->with() + ->willReturn($token); + + $this->encodedContextFactoryMock + ->expects($this->exactly(2)) + ->method('create') + ->with($this->callback($registerInitializationVector)) + ->willReturn($this->encodedContextMock); + + $this->assertSame($this->encodedContextMock, $this->cryptographer->encode($this->source)); + $this->assertSame($this->encodedContextMock, $this->cryptographer->encode($this->source)); + $this->assertCount(2, array_unique($this->initializationVectors)); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @dataProvider encodeNotValidSourceDataProvider + */ + public function testEncodeNotValidSource($source) + { + $this->cryptographer->encode($source); + } + + /** + * @return array + */ + public function encodeNotValidSourceDataProvider() + { + return [ + 'Array' => [[]], + 'Empty string' => [''], + ]; + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testEncodeNotValidCipherMethod() + { + $source = 'Some string'; + $cryptographer = $this->objectManagerHelper->getObject( + Cryptographer::class, + [ + 'cipherMethod' => 'Wrong-method', + ] + ); + + $cryptographer->encode($source); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testEncodeTokenNotValid() + { + $source = 'Some string'; + + $this->analyticsTokenMock + ->expects($this->once()) + ->method('getToken') + ->with() + ->willReturn(null); + + $this->cryptographer->encode($source); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/EncodedContextTest.php b/app/code/Magento/Analytics/Test/Unit/Model/EncodedContextTest.php new file mode 100644 index 0000000000000..a3ea8b040c992 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/EncodedContextTest.php @@ -0,0 +1,61 @@ +objectManagerHelper = new ObjectManagerHelper($this); + } + + /** + * @param string $content + * @param string|null $initializationVector + * @return void + * @dataProvider constructDataProvider + */ + public function testConstruct($content, $initializationVector) + { + $constructorArguments = [ + 'content' => $content, + 'initializationVector' => $initializationVector, + ]; + /** @var EncodedContext $encodedContext */ + $encodedContext = $this->objectManagerHelper->getObject( + EncodedContext::class, + array_filter($constructorArguments) + ); + + $this->assertSame($content, $encodedContext->getContent()); + $this->assertSame($initializationVector ?: '', $encodedContext->getInitializationVector()); + } + + /** + * @return array + */ + public function constructDataProvider() + { + return [ + 'Without Initialization Vector' => ['content text', null], + 'With Initialization Vector' => ['content text', 'c51sd3c4sd68c5sd'], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/ExportDataHandlerTest.php b/app/code/Magento/Analytics/Test/Unit/Model/ExportDataHandlerTest.php new file mode 100644 index 0000000000000..21fe5fdf8081c --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/ExportDataHandlerTest.php @@ -0,0 +1,270 @@ +filesystemMock = $this->getMockBuilder(Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->archiveMock = $this->getMockBuilder(Archive::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->reportWriterMock = $this->getMockBuilder(ReportWriterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cryptographerMock = $this->getMockBuilder(Cryptographer::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileRecorderMock = $this->getMockBuilder(FileRecorder::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->directoryMock = $this->getMockBuilder(WriteInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->encodedContextMock = $this->getMockBuilder(EncodedContext::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->exportDataHandler = $this->objectManagerHelper->getObject( + ExportDataHandler::class, + [ + 'filesystem' => $this->filesystemMock, + 'archive' => $this->archiveMock, + 'reportWriter' => $this->reportWriterMock, + 'cryptographer' => $this->cryptographerMock, + 'fileRecorder' => $this->fileRecorderMock, + 'subdirectoryPath' => $this->subdirectoryPath, + 'archiveName' => $this->archiveName, + ] + ); + } + + /** + * @param bool $isArchiveSourceDirectory + * @dataProvider prepareExportDataDataProvider + */ + public function testPrepareExportData($isArchiveSourceDirectory) + { + $tmpFilesDirectoryPath = $this->subdirectoryPath . 'tmp/'; + $archiveRelativePath = $this->subdirectoryPath . $this->archiveName; + + $archiveSource = $isArchiveSourceDirectory ? (__DIR__) : '/tmp/' . $tmpFilesDirectoryPath; + $archiveAbsolutePath = '/tmp/' . $archiveRelativePath; + + $this->filesystemMock + ->expects($this->once()) + ->method('getDirectoryWrite') + ->with(DirectoryList::SYS_TMP) + ->willReturn($this->directoryMock); + $this->directoryMock + ->expects($this->exactly(4)) + ->method('delete') + ->withConsecutive( + [$tmpFilesDirectoryPath], + [$archiveRelativePath] + ); + + $this->directoryMock + ->expects($this->exactly(4)) + ->method('getAbsolutePath') + ->withConsecutive( + [$tmpFilesDirectoryPath], + [$tmpFilesDirectoryPath], + [$archiveRelativePath], + [$archiveRelativePath] + ) + ->willReturnOnConsecutiveCalls( + $archiveSource, + $archiveSource, + $archiveAbsolutePath, + $archiveAbsolutePath + ); + + $this->reportWriterMock + ->expects($this->once()) + ->method('write') + ->with($this->directoryMock, $tmpFilesDirectoryPath); + + $this->directoryMock + ->expects($this->exactly(2)) + ->method('isExist') + ->withConsecutive( + [$tmpFilesDirectoryPath], + [$archiveRelativePath] + ) + ->willReturnOnConsecutiveCalls( + true, + true + ); + + $this->directoryMock + ->expects($this->once()) + ->method('create') + ->with(dirname($archiveRelativePath)); + + $this->archiveMock + ->expects($this->once()) + ->method('pack') + ->with( + $archiveSource, + $archiveAbsolutePath, + $isArchiveSourceDirectory ? true: false + ); + + $fileContent = 'Some text'; + $this->directoryMock + ->expects($this->once()) + ->method('readFile') + ->with($archiveRelativePath) + ->willReturn($fileContent); + + $this->cryptographerMock + ->expects($this->once()) + ->method('encode') + ->with($fileContent) + ->willReturn($this->encodedContextMock); + + $this->fileRecorderMock + ->expects($this->once()) + ->method('recordNewFile') + ->with($this->encodedContextMock); + + $this->assertTrue($this->exportDataHandler->prepareExportData()); + } + + /** + * @return array + */ + public function prepareExportDataDataProvider() + { + return [ + 'Data source for archive is directory' => [true], + 'Data source for archive doesn\'t directory' => [false], + ]; + } + + /** + * @return void + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testPrepareExportDataWithLocalizedException() + { + $tmpFilesDirectoryPath = $this->subdirectoryPath . 'tmp/'; + $archivePath = $this->subdirectoryPath . $this->archiveName; + + $this->filesystemMock + ->expects($this->once()) + ->method('getDirectoryWrite') + ->with(DirectoryList::SYS_TMP) + ->willReturn($this->directoryMock); + $this->reportWriterMock + ->expects($this->once()) + ->method('write') + ->with($this->directoryMock, $tmpFilesDirectoryPath); + $this->directoryMock + ->expects($this->exactly(3)) + ->method('delete') + ->withConsecutive( + [$tmpFilesDirectoryPath], + [$tmpFilesDirectoryPath], + [$archivePath] + ); + $this->directoryMock + ->expects($this->exactly(2)) + ->method('getAbsolutePath') + ->with($tmpFilesDirectoryPath); + $this->directoryMock + ->expects($this->once()) + ->method('isExist') + ->with($tmpFilesDirectoryPath) + ->willReturn(false); + + $this->assertNull($this->exportDataHandler->prepareExportData()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/FileInfoManagerTest.php b/app/code/Magento/Analytics/Test/Unit/Model/FileInfoManagerTest.php new file mode 100644 index 0000000000000..79617efff51ed --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/FileInfoManagerTest.php @@ -0,0 +1,194 @@ +flagManagerMock = $this->getMockBuilder(FlagManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileInfoFactoryMock = $this->getMockBuilder(FileInfoFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileInfoMock = $this->getMockBuilder(FileInfo::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->fileInfoManager = $this->objectManagerHelper->getObject( + FileInfoManager::class, + [ + 'flagManager' => $this->flagManagerMock, + 'fileInfoFactory' => $this->fileInfoFactoryMock, + 'flagCode' => $this->flagCode, + 'encodedParameters' => $this->encodedParameters, + ] + ); + } + + /** + * @return void + */ + public function testSave() + { + $path = 'path/to/file'; + $initializationVector = openssl_random_pseudo_bytes(16); + $parameters = [ + 'path' => $path, + 'initializationVector' => $initializationVector, + ]; + + $this->fileInfoMock + ->expects($this->once()) + ->method('getPath') + ->with() + ->willReturn($path); + $this->fileInfoMock + ->expects($this->once()) + ->method('getInitializationVector') + ->with() + ->willReturn($initializationVector); + + foreach ($this->encodedParameters as $encodedParameter) { + $parameters[$encodedParameter] = base64_encode($parameters[$encodedParameter]); + } + $this->flagManagerMock + ->expects($this->once()) + ->method('saveFlag') + ->with($this->flagCode, $parameters); + + $this->assertTrue($this->fileInfoManager->save($this->fileInfoMock)); + } + + /** + * @param string|null $path + * @param string|null $initializationVector + * @dataProvider saveWithLocalizedExceptionDataProvider + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testSaveWithLocalizedException($path, $initializationVector) + { + $this->fileInfoMock + ->expects($this->once()) + ->method('getPath') + ->with() + ->willReturn($path); + $this->fileInfoMock + ->expects($this->once()) + ->method('getInitializationVector') + ->with() + ->willReturn($initializationVector); + + $this->fileInfoManager->save($this->fileInfoMock); + } + + /** + * @return array + */ + public function saveWithLocalizedExceptionDataProvider() + { + return [ + 'Empty FileInfo' => [null, null], + 'FileInfo without IV' => ['path/to/file', null], + ]; + } + + /** + * @dataProvider loadDataProvider + * @param array|null $parameters + */ + public function testLoad($parameters) + { + $this->flagManagerMock + ->expects($this->once()) + ->method('getFlagData') + ->with($this->flagCode) + ->willReturn($parameters); + + $processedParameters = $parameters ?: []; + $encodedParameters = array_intersect($this->encodedParameters, array_keys($processedParameters)); + foreach ($encodedParameters as $encodedParameter) { + $processedParameters[$encodedParameter] = base64_decode($processedParameters[$encodedParameter]); + } + + $this->fileInfoFactoryMock + ->expects($this->once()) + ->method('create') + ->with($processedParameters) + ->willReturn($this->fileInfoMock); + + $this->assertSame($this->fileInfoMock, $this->fileInfoManager->load()); + } + + /** + * @return array + */ + public function loadDataProvider() + { + return [ + 'Empty flag data' => [null], + 'Correct flag data' => [[ + 'path' => 'path/to/file', + 'initializationVector' => 'xUJjl54MVke+FvMFSBpRSA==', + ]], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/FileInfoTest.php b/app/code/Magento/Analytics/Test/Unit/Model/FileInfoTest.php new file mode 100644 index 0000000000000..282ab67a523dc --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/FileInfoTest.php @@ -0,0 +1,62 @@ +objectManagerHelper = new ObjectManagerHelper($this); + } + + /** + * @param string|null $path + * @param string|null $initializationVector + * @return void + * @dataProvider constructDataProvider + */ + public function testConstruct($path, $initializationVector) + { + $constructorArguments = [ + 'path' => $path, + 'initializationVector' => $initializationVector, + ]; + /** @var FileInfo $fileInfo */ + $fileInfo = $this->objectManagerHelper->getObject( + FileInfo::class, + array_filter($constructorArguments) + ); + + $this->assertSame($path?:'', $fileInfo->getPath()); + $this->assertSame($initializationVector?:'', $fileInfo->getInitializationVector()); + } + + /** + * @return array + */ + public function constructDataProvider() + { + return [ + 'Degenerate object' => [null, null], + 'Without Initialization Vector' => ['content text', null], + 'With Initialization Vector' => ['content text', 'c51sd3c4sd68c5sd'], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/FileRecorderTest.php b/app/code/Magento/Analytics/Test/Unit/Model/FileRecorderTest.php new file mode 100644 index 0000000000000..244b46439a492 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/FileRecorderTest.php @@ -0,0 +1,209 @@ +fileInfoManagerMock = $this->getMockBuilder(FileInfoManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileInfoFactoryMock = $this->getMockBuilder(FileInfoFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $this->filesystemMock = $this->getMockBuilder(Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileInfoMock = $this->getMockBuilder(FileInfo::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->directoryMock = $this->getMockBuilder(WriteInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->encodedContextMock = $this->getMockBuilder(EncodedContext::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->fileRecorder = $this->objectManagerHelper->getObject( + FileRecorder::class, + [ + 'fileInfoManager' => $this->fileInfoManagerMock, + 'fileInfoFactory' => $this->fileInfoFactoryMock, + 'filesystem' => $this->filesystemMock, + 'fileSubdirectoryPath' => $this->fileSubdirectoryPath, + 'encodedFileName' => $this->encodedFileName, + ] + ); + } + + /** + * @param string $pathToExistingFile + * @dataProvider recordNewFileDataProvider + */ + public function testRecordNewFile($pathToExistingFile) + { + $content = openssl_random_pseudo_bytes(200); + + $this->filesystemMock + ->expects($this->once()) + ->method('getDirectoryWrite') + ->with(DirectoryList::MEDIA) + ->willReturn($this->directoryMock); + + $this->encodedContextMock + ->expects($this->once()) + ->method('getContent') + ->with() + ->willReturn($content); + + $hashLength = 64; + $fileRelativePathPattern = '#' . preg_quote($this->fileSubdirectoryPath, '#') + . '.{' . $hashLength . '}/' . preg_quote($this->encodedFileName, '#') .'#'; + $this->directoryMock + ->expects($this->once()) + ->method('writeFile') + ->with($this->matchesRegularExpression($fileRelativePathPattern), $content) + ->willReturn($this->directoryMock); + + $this->fileInfoManagerMock + ->expects($this->once()) + ->method('load') + ->with() + ->willReturn($this->fileInfoMock); + + $this->encodedContextMock + ->expects($this->once()) + ->method('getInitializationVector') + ->with() + ->willReturn('init_vector***'); + + /** register file */ + $this->fileInfoFactoryMock + ->expects($this->once()) + ->method('create') + ->with($this->callback( + function ($parameters) { + return !empty($parameters['path']) && ('init_vector***' === $parameters['initializationVector']); + } + )) + ->willReturn($this->fileInfoMock); + $this->fileInfoManagerMock + ->expects($this->once()) + ->method('save') + ->with($this->fileInfoMock); + + /** remove old file */ + $this->fileInfoMock + ->expects($this->exactly($pathToExistingFile ? 3 : 1)) + ->method('getPath') + ->with() + ->willReturn($pathToExistingFile); + $directoryName = dirname($pathToExistingFile); + if ($directoryName === '.') { + $this->directoryMock + ->expects($this->once()) + ->method('delete') + ->with($pathToExistingFile); + } elseif ($directoryName) { + $this->directoryMock + ->expects($this->exactly(2)) + ->method('delete') + ->withConsecutive( + [$pathToExistingFile], + [$directoryName] + ); + } + + $this->assertTrue($this->fileRecorder->recordNewFile($this->encodedContextMock)); + } + + /** + * @return array + */ + public function recordNewFileDataProvider() + { + return [ + 'File doesn\'t exist' => [''], + 'Existing file into subdirectory' => ['dir_name/file.txt'], + 'Existing file doesn\'t into subdirectory' => ['file.txt'], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/FlagManagerTest.php b/app/code/Magento/Analytics/Test/Unit/Model/FlagManagerTest.php new file mode 100644 index 0000000000000..77abb0c87e1ea --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/FlagManagerTest.php @@ -0,0 +1,123 @@ +flagFactoryMock = $this->getMockBuilder(FlagFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->flagResourceMock = $this->getMockBuilder(FlagResource::class) + ->disableOriginalConstructor() + ->getMock(); + $this->flagMock = $this->getMockBuilder(Flag::class) + ->disableOriginalConstructor() + ->getMock(); + $this->flagManager = new FlagManager( + $this->flagFactoryMock, + $this->flagResourceMock + ); + } + + public function testGetFlagData() + { + $flagCode = "flag"; + $this->setupFlagObject($flagCode); + $this->flagMock->expects($this->once()) + ->method('getFlagData') + ->willReturn(10); + $this->assertEquals($this->flagManager->getFlagData($flagCode), 10); + } + + public function testSaveFlag() + { + $flagCode = "flag"; + $this->setupFlagObject($flagCode); + $this->flagMock->expects($this->once()) + ->method('setFlagData') + ->with(10); + $this->flagResourceMock->expects($this->once()) + ->method('save') + ->with($this->flagMock); + $this->assertTrue($this->flagManager->saveFlag($flagCode, 10)); + } + + /** + * @dataProvider flagExistDataProvider + * + * @param bool $isFlagExist + */ + public function testDeleteFlag($isFlagExist) + { + $flagCode = "flag"; + $this->setupFlagObject($flagCode); + $this->flagMock + ->expects($this->once()) + ->method('getId') + ->willReturn($isFlagExist); + if ($isFlagExist) { + $this->flagResourceMock + ->expects($this->once()) + ->method('delete') + ->with($this->flagMock); + } + $this->assertTrue($this->flagManager->deleteFlag($flagCode)); + } + + private function setupFlagObject($flagCode) + { + $this->flagFactoryMock->expects($this->once()) + ->method('create') + ->with(['data' => ['flag_code' => $flagCode]]) + ->willReturn($this->flagMock); + $this->flagResourceMock->expects($this->once()) + ->method('load') + ->with($this->flagMock, $flagCode, 'flag_code'); + } + + /** + * Provide variations of the flag existence. + * + * @return array + */ + public function flagExistDataProvider() + { + return [ + [true], + [false] + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/IntegrationManagerTest.php b/app/code/Magento/Analytics/Test/Unit/Model/IntegrationManagerTest.php new file mode 100644 index 0000000000000..14af7c7e66e99 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/IntegrationManagerTest.php @@ -0,0 +1,228 @@ +integrationServiceMock = $this->getMockBuilder(IntegrationServiceInterface::class) + ->getMock(); + $this->configMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->oauthServiceMock = $this->getMockBuilder(OauthServiceInterface::class) + ->getMock(); + $this->integrationMock = $this->getMockBuilder(Integration::class) + ->disableOriginalConstructor() + ->setMethods([ + 'getId', + 'getConsumerId' + ]) + ->getMock(); + $this->integrationManager = $objectManagerHelper->getObject( + IntegrationManager::class, + [ + 'integrationService' => $this->integrationServiceMock, + 'oauthService' => $this->oauthServiceMock, + 'config' => $this->configMock + ] + ); + } + + /** + * @param string $status + * + * @return array + */ + private function getIntegrationUserData($status) + { + return [ + 'name' => 'ma-integration-user', + 'status' => $status, + 'all_resources' => false, + 'resource' => [ + 'Magento_Analytics::analytics', + 'Magento_Analytics::analytics_api' + ], + ]; + } + + /** + * @return void + */ + public function testActivateIntegrationSuccess() + { + $this->integrationServiceMock->expects($this->once()) + ->method('findByName') + ->with('ma-integration-user') + ->willReturn($this->integrationMock); + $this->integrationMock->expects($this->exactly(2)) + ->method('getId') + ->willReturn(100500); + $integrationData = $this->getIntegrationUserData(Integration::STATUS_ACTIVE); + $integrationData['integration_id'] = 100500; + $this->configMock->expects($this->exactly(2)) + ->method('getConfigDataValue') + ->with('analytics/integration_name', null, null) + ->willReturn('ma-integration-user'); + $this->integrationServiceMock->expects($this->once()) + ->method('update') + ->with($integrationData); + $this->assertTrue($this->integrationManager->activateIntegration()); + } + + /** + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + */ + public function testActivateIntegrationFailureNoSuchEntity() + { + $this->integrationServiceMock->expects($this->once()) + ->method('findByName') + ->with('ma-integration-user') + ->willReturn($this->integrationMock); + $this->integrationMock->expects($this->once()) + ->method('getId') + ->willReturn(null); + $this->configMock->expects($this->once()) + ->method('getConfigDataValue') + ->with('analytics/integration_name', null, null) + ->willReturn('ma-integration-user'); + $this->integrationServiceMock->expects($this->never()) + ->method('update'); + $this->integrationManager->activateIntegration(); + } + + /** + * @dataProvider integrationIdDataProvider + * + * @param int|null $integrationId If null integration is absent. + * @return void + */ + public function testGetTokenNewIntegration($integrationId) + { + $this->configMock->expects($this->atLeastOnce()) + ->method('getConfigDataValue') + ->with('analytics/integration_name', null, null) + ->willReturn('ma-integration-user'); + $this->integrationServiceMock->expects($this->once()) + ->method('findByName') + ->with('ma-integration-user') + ->willReturn($this->integrationMock); + $this->integrationMock->expects($this->once()) + ->method('getConsumerId') + ->willReturn(100500); + $this->integrationMock->expects($this->once()) + ->method('getId') + ->willReturn($integrationId); + if (!$integrationId) { + $this->integrationServiceMock + ->expects($this->once()) + ->method('create') + ->with($this->getIntegrationUserData(Integration::STATUS_INACTIVE)) + ->willReturn($this->integrationMock); + } + $this->oauthServiceMock->expects($this->at(0)) + ->method('getAccessToken') + ->with(100500) + ->willReturn(false); + $this->oauthServiceMock->expects($this->at(2)) + ->method('getAccessToken') + ->with(100500) + ->willReturn('IntegrationToken'); + $this->oauthServiceMock->expects($this->once()) + ->method('createAccessToken') + ->with(100500, true) + ->willReturn(true); + $this->assertEquals('IntegrationToken', $this->integrationManager->generateToken()); + } + + /** + * @dataProvider integrationIdDataProvider + * + * @param int|null $integrationId If null integration is absent. + * @return void + */ + public function testGetTokenExistingIntegration($integrationId) + { + $this->configMock->expects($this->atLeastOnce()) + ->method('getConfigDataValue') + ->with('analytics/integration_name', null, null) + ->willReturn('ma-integration-user'); + $this->integrationServiceMock->expects($this->once()) + ->method('findByName') + ->with('ma-integration-user') + ->willReturn($this->integrationMock); + $this->integrationMock->expects($this->once()) + ->method('getConsumerId') + ->willReturn(100500); + $this->integrationMock->expects($this->once()) + ->method('getId') + ->willReturn($integrationId); + if (!$integrationId) { + $this->integrationServiceMock + ->expects($this->once()) + ->method('create') + ->with($this->getIntegrationUserData(Integration::STATUS_INACTIVE)) + ->willReturn($this->integrationMock); + } + $this->oauthServiceMock->expects($this->once()) + ->method('getAccessToken') + ->with(100500) + ->willReturn('IntegrationToken'); + $this->oauthServiceMock->expects($this->never()) + ->method('createAccessToken'); + $this->assertEquals('IntegrationToken', $this->integrationManager->generateToken()); + } + + /** + * @return array + */ + public function integrationIdDataProvider() + { + return [ + [1], + [null], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/LinkProviderTest.php b/app/code/Magento/Analytics/Test/Unit/Model/LinkProviderTest.php new file mode 100644 index 0000000000000..e0ae69c975f7e --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/LinkProviderTest.php @@ -0,0 +1,162 @@ +linkInterfaceFactoryMock = $this->getMockBuilder(LinkInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->fileInfoManagerMock = $this->getMockBuilder(FileInfoManager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManagerInterfaceMock = $this->getMockBuilder(StoreManagerInterface::class) + ->getMockForAbstractClass(); + $this->linkInterfaceMock = $this->getMockBuilder(LinkInterface::class) + ->getMockForAbstractClass(); + $this->fileInfoMock = $this->getMockBuilder(FileInfo::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeMock = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->getMock(); + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->linkProvider = $this->objectManagerHelper->getObject( + LinkProvider::class, + [ + 'linkInterfaceFactory' => $this->linkInterfaceFactoryMock, + 'fileInfoManager' => $this->fileInfoManagerMock, + 'storeManager' => $this->storeManagerInterfaceMock + ] + ); + } + + public function testGet() + { + $baseUrl = 'http://magento.local/pub/media/'; + $fileInfoPath = 'analytics/data.tgz'; + $fileInitializationVector = 'er312esq23eqq'; + $this->fileInfoManagerMock->expects($this->once()) + ->method('load') + ->willReturn($this->fileInfoMock); + $this->linkInterfaceFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->linkInterfaceMock); + $this->storeManagerInterfaceMock->expects($this->once()) + ->method('getStore')->willReturn($this->storeMock); + $this->storeMock->expects($this->once()) + ->method('getBaseUrl') + ->with( + UrlInterface::URL_TYPE_MEDIA + ) + ->willReturn($baseUrl); + $this->fileInfoMock->expects($this->atLeastOnce()) + ->method('getPath') + ->willReturn($fileInfoPath); + $this->fileInfoMock->expects($this->atLeastOnce()) + ->method('getInitializationVector') + ->willReturn($fileInitializationVector); + $this->linkInterfaceMock->expects($this->once())->method('setUrl')->with( + $baseUrl . $fileInfoPath + ); + $this->linkInterfaceMock->expects($this->once()) + ->method('setInitializationVector') + ->with(base64_encode($fileInitializationVector)); + $this->assertEquals($this->linkInterfaceMock, $this->linkProvider->get()); + } + + /** + * @param string|null $fileInfoPath + * @param string|null $fileInitializationVector + * + * @dataProvider fileNotReadyDataProvider + * @expectedException \Magento\Framework\Webapi\Exception + * @expectedExceptionMessage File is not ready yet. + */ + public function testFileNotReady($fileInfoPath, $fileInitializationVector) + { + $this->fileInfoManagerMock->expects($this->once()) + ->method('load') + ->willReturn($this->fileInfoMock); + $this->fileInfoMock->expects($this->once()) + ->method('getPath') + ->willReturn($fileInfoPath); + $this->fileInfoMock->expects($this->any()) + ->method('getInitializationVector') + ->willReturn($fileInitializationVector); + $this->linkProvider->get(); + } + + /** + * @return array + */ + public function fileNotReadyDataProvider() + { + return [ + [null, 'initVector'], + ['path', null] + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/NotificationTimeTest.php b/app/code/Magento/Analytics/Test/Unit/Model/NotificationTimeTest.php new file mode 100644 index 0000000000000..78bb694645fe8 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/NotificationTimeTest.php @@ -0,0 +1,76 @@ +flagManagerMock = $this->getMockBuilder(FlagManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $objectManagerHelper = new ObjectManagerHelper($this); + $this->notificationTime = $objectManagerHelper->getObject( + NotificationTime::class, + [ + 'flagManager' => $this->flagManagerMock, + ] + ); + } + + public function testStoreLastTimeNotification() + { + $value = 100500; + + $this->flagManagerMock + ->expects($this->once()) + ->method('saveFlag') + ->with(NotificationTime::NOTIFICATION_TIME, $value) + ->willReturn(true); + $this->assertTrue($this->notificationTime->storeLastTimeNotification($value)); + } + + public function testGetLastTimeNotification() + { + $value = 100500; + + $this->flagManagerMock + ->expects($this->once()) + ->method('getFlagData') + ->with(NotificationTime::NOTIFICATION_TIME) + ->willReturn(true); + $this->assertEquals($value, $this->notificationTime->getLastTimeNotification()); + } + + public function testUnsetLastTimeNotificationValue() + { + $this->flagManagerMock + ->expects($this->once()) + ->method('deleteFlag') + ->with(NotificationTime::NOTIFICATION_TIME) + ->willReturn(true); + $this->assertTrue($this->notificationTime->unsetLastTimeNotificationValue()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/Plugin/BaseUrlConfigPluginTest.php b/app/code/Magento/Analytics/Test/Unit/Model/Plugin/BaseUrlConfigPluginTest.php new file mode 100644 index 0000000000000..bf6456610f7a3 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/Plugin/BaseUrlConfigPluginTest.php @@ -0,0 +1,190 @@ +flagManagerMock = $this->getMockBuilder(FlagManager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->configValueMock = $this->getMockBuilder(Baseurl::class) + ->disableOriginalConstructor() + ->getMock(); + $this->subscriptionStatusProvider = $this->getMockBuilder(SubscriptionStatusProvider::class) + ->disableOriginalConstructor() + ->getMock(); + $this->configWriterMock = $this->getMockBuilder(WriterInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->plugin = $this->objectManagerHelper->getObject( + BaseUrlConfigPlugin::class, + [ + 'flagManager' => $this->flagManagerMock, + 'subscriptionStatusProvider' => $this->subscriptionStatusProvider, + 'configWriter' => $this->configWriterMock + ] + ); + } + + /** + * @param array $testData + * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $saveConfigInvokeMatcher + * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $oldValueInvokeMatcher + * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $saveFlagInvokeMatcher + * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $configValueGetPathMatcher + * + * @return void + * @dataProvider pluginDataProvider + */ + public function testPluginForAfterSave( + array $testData, + \PHPUnit_Framework_MockObject_Matcher_InvokedCount $saveConfigInvokeMatcher, + \PHPUnit_Framework_MockObject_Matcher_InvokedCount $oldValueInvokeMatcher, + \PHPUnit_Framework_MockObject_Matcher_InvokedCount $saveFlagInvokeMatcher, + \PHPUnit_Framework_MockObject_Matcher_InvokedCount $configValueGetPathMatcher + ) { + $this->configValueMock->expects($this->once()) + ->method('isValueChanged') + ->willReturn($testData['isValueChanged']); + + $this->configValueMock->expects($configValueGetPathMatcher) + ->method('getData') + ->with('path') + ->willReturn($testData['path']); + $this->subscriptionStatusProvider->expects($this->any())->method('getStatus') + ->willReturn($testData['subscriptionStatus']); + + $oldUrl = 'mage.dev'; + $this->configValueMock->expects($oldValueInvokeMatcher) + ->method('getOldValue') + ->willReturn($oldUrl); + $this->flagManagerMock->expects($saveFlagInvokeMatcher) + ->method('saveFlag') + ->with(BaseUrlConfigPlugin::OLD_BASE_URL_FLAG_CODE, $oldUrl); + + $this->configWriterMock->expects($saveConfigInvokeMatcher)->method('save') + ->with( + BaseUrlConfigPlugin::UPDATE_CRON_STRING_PATH, + '0 * * * *' + ); + + $this->assertEquals( + $this->configValueMock, + $this->plugin->afterAfterSave($this->configValueMock, $this->configValueMock) + ); + } + + /** + * @return array + */ + public function pluginDataProvider() + { + return [ + 'setup_subscription_update_cron_job' => [ + 'testData' => [ + 'isValueChanged' => true, + 'subscriptionStatus' => SubscriptionStatusProvider::ENABLED, + 'path' => Store::XML_PATH_SECURE_BASE_URL + ], + 'saveConfigInvokeMatcher' => $this->once(), + 'oldValueInvokeMatcher' => $this->once(), + 'saveFlagInvokeMatcher' => $this->once(), + 'configValueGetPathMatcher' => $this->once(), + ], + 'base_url_not_changed' => [ + 'testData' => [ + 'isValueChanged' => false, + 'subscriptionStatus' => SubscriptionStatusProvider::ENABLED, + 'path' => Store::XML_PATH_SECURE_BASE_URL + ], + 'saveConfigInvokeMatcher' => $this->never(), + 'oldValueInvokeMatcher' => $this->never(), + 'saveFlagInvokeMatcher' => $this->never(), + 'configValueGetPathMatcher' => $this->never(), + ], + 'analytics_disabled' => [ + 'testData' => [ + 'isValueChanged' => true, + 'subscriptionStatus' => SubscriptionStatusProvider::DISABLED, + 'path' => Store::XML_PATH_SECURE_BASE_URL + ], + 'saveConfigInvokeMatcher' => $this->never(), + 'oldValueInvokeMatcher' => $this->never(), + 'saveFlagInvokeMatcher' => $this->never(), + 'configValueGetPathMatcher' => $this->once(), + ], + 'analytics_pending' => [ + 'testData' => [ + 'isValueChanged' => true, + 'subscriptionStatus' => SubscriptionStatusProvider::PENDING, + 'path' => Store::XML_PATH_SECURE_BASE_URL + ], + 'saveConfigInvokeMatcher' => $this->never(), + 'oldValueInvokeMatcher' => $this->never(), + 'saveFlagInvokeMatcher' => $this->never(), + 'configValueGetPathMatcher' => $this->once(), + ], + 'unsecure_url_changed' => [ + 'testData' => [ + 'isValueChanged' => true, + 'subscriptionStatus' => SubscriptionStatusProvider::PENDING, + 'path' => Store::XML_PATH_UNSECURE_BASE_URL + ], + 'saveConfigInvokeMatcher' => $this->never(), + 'oldValueInvokeMatcher' => $this->never(), + 'saveFlagInvokeMatcher' => $this->never(), + 'configValueGetPathMatcher' => $this->once(), + ] + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/ReportUrlProviderTest.php b/app/code/Magento/Analytics/Test/Unit/Model/ReportUrlProviderTest.php new file mode 100644 index 0000000000000..4f3648a776b53 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/ReportUrlProviderTest.php @@ -0,0 +1,122 @@ +configMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->analyticsTokenMock = $this->getMockBuilder(AnalyticsToken::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->otpRequestMock = $this->getMockBuilder(OTPRequest::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->reportUrlProvider = $this->objectManagerHelper->getObject( + ReportUrlProvider::class, + [ + 'config' => $this->configMock, + 'analyticsToken' => $this->analyticsTokenMock, + 'otpRequest' => $this->otpRequestMock, + 'urlReportConfigPath' => $this->urlReportConfigPath, + ] + ); + } + + /** + * @param bool $isTokenExist + * @param string|null $otp If null OTP was not received. + * + * @dataProvider getUrlDataProvider + */ + public function testGetUrl($isTokenExist, $otp) + { + $reportUrl = 'https://example.com/report'; + $url = ''; + + $this->configMock + ->expects($this->once()) + ->method('getValue') + ->with($this->urlReportConfigPath) + ->willReturn($reportUrl); + $this->analyticsTokenMock + ->expects($this->once()) + ->method('isTokenExist') + ->with() + ->willReturn($isTokenExist); + $this->otpRequestMock + ->expects($isTokenExist ? $this->once() : $this->never()) + ->method('call') + ->with() + ->willReturn($otp); + if ($isTokenExist && $otp) { + $url = $reportUrl . '?' . http_build_query(['otp' => $otp], '', '&'); + } + $this->assertSame($url ?: $reportUrl, $this->reportUrlProvider->getUrl()); + } + + /** + * @return array + */ + public function getUrlDataProvider() + { + return [ + 'TokenDoesNotExist' => [false, null], + 'TokenExistAndOtpEmpty' => [true, null], + 'TokenExistAndOtpValid' => [true, '249e6b658877bde2a77bc4ab'], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/ReportWriterTest.php b/app/code/Magento/Analytics/Test/Unit/Model/ReportWriterTest.php new file mode 100644 index 0000000000000..4f9a9aaf185a6 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/ReportWriterTest.php @@ -0,0 +1,213 @@ +configInterfaceMock = $this->getMockBuilder(ConfigInterface::class)->getMockForAbstractClass(); + $this->reportValidatorMock = $this->getMockBuilder(ReportValidator::class) + ->disableOriginalConstructor()->getMock(); + $this->providerFactoryMock = $this->getMockBuilder(ProviderFactory::class) + ->disableOriginalConstructor()->getMock(); + $this->reportProviderMock = $this->getMockBuilder(ReportProvider::class) + ->disableOriginalConstructor()->getMock(); + $this->directoryMock = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->reportWriter = $this->objectManagerHelper->getObject( + ReportWriter::class, + [ + 'config' => $this->configInterfaceMock, + 'reportValidator' => $this->reportValidatorMock, + 'providerFactory' => $this->providerFactoryMock + ] + ); + } + + /** + * @param array $configData + * @return void + * + * @dataProvider configDataProvider + */ + public function testWrite(array $configData) + { + $errors = []; + $fileData = [ + ['number' => 1, 'type' => 'Shoes Usual'] + ]; + $this->configInterfaceMock + ->expects($this->once()) + ->method('get') + ->with() + ->willReturn([$configData]); + $this->providerFactoryMock + ->expects($this->once()) + ->method('create') + ->with($this->providerClass) + ->willReturn($this->reportProviderMock); + $parameterName = isset(reset($configData)[0]['parameters']['name']) + ? reset($configData)[0]['parameters']['name'] + : ''; + $this->reportProviderMock->expects($this->once()) + ->method('getReport') + ->with($parameterName ?: null) + ->willReturn($fileData); + $errorStreamMock = $this->getMockBuilder( + \Magento\Framework\Filesystem\File\WriteInterface::class + )->getMockForAbstractClass(); + $errorStreamMock + ->expects($this->once()) + ->method('lock') + ->with(); + $errorStreamMock + ->expects($this->exactly(2)) + ->method('writeCsv') + ->withConsecutive( + [array_keys($fileData[0])], + [$fileData[0]] + ); + $errorStreamMock->expects($this->once())->method('unlock'); + $errorStreamMock->expects($this->once())->method('close'); + if ($parameterName) { + $this->reportValidatorMock + ->expects($this->once()) + ->method('validate') + ->with($parameterName) + ->willReturn($errors); + } + $this->directoryMock + ->expects($this->once()) + ->method('openFile') + ->with( + $this->stringContains('/var/tmp' . $parameterName ?: $this->reportName), + 'w+' + )->willReturn($errorStreamMock); + $this->assertTrue($this->reportWriter->write($this->directoryMock, '/var/tmp')); + } + + /** + * @param array $configData + * @return void + * + * @dataProvider configDataProvider + */ + public function testWriteErrorFile($configData) + { + $errors = ['orders', 'SQL Error: test']; + $this->configInterfaceMock->expects($this->once())->method('get')->willReturn([$configData]); + $errorStreamMock = $this->getMockBuilder( + \Magento\Framework\Filesystem\File\WriteInterface::class + )->getMockForAbstractClass(); + $errorStreamMock->expects($this->once())->method('lock'); + $errorStreamMock->expects($this->once())->method('writeCsv')->with($errors); + $errorStreamMock->expects($this->once())->method('unlock'); + $errorStreamMock->expects($this->once())->method('close'); + $this->reportValidatorMock->expects($this->once())->method('validate')->willReturn($errors); + $this->directoryMock->expects($this->once())->method('openFile')->with('/var/tmp' . 'errors.csv', 'w+') + ->willReturn($errorStreamMock); + $this->assertTrue($this->reportWriter->write($this->directoryMock, '/var/tmp')); + } + + /** + * @return void + */ + public function testWriteEmptyReports() + { + $this->configInterfaceMock->expects($this->once())->method('get')->willReturn([]); + $this->reportValidatorMock->expects($this->never())->method('validate'); + $this->directoryMock->expects($this->never())->method('openFile'); + $this->assertTrue($this->reportWriter->write($this->directoryMock, '/var/tmp')); + } + + /** + * @return array + */ + public function configDataProvider() + { + return [ + 'reportProvider' => [ + [ + 'providers' => [ + [ + 'name' => $this->providerName, + 'class' => $this->providerClass, + 'parameters' => [ + 'name' => $this->reportName + ], + ] + ] + ] + ], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/ReportXml/ModuleIteratorTest.php b/app/code/Magento/Analytics/Test/Unit/Model/ReportXml/ModuleIteratorTest.php new file mode 100644 index 0000000000000..7bd6a0c5d5534 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/ReportXml/ModuleIteratorTest.php @@ -0,0 +1,50 @@ +moduleManagerMock = $this->getMockBuilder(ModuleManager::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManagerHelper = new ObjectManagerHelper($this); + $this->moduleIterator = $objectManagerHelper->getObject( + ModuleIterator::class, + [ + 'moduleManager' => $this->moduleManagerMock, + 'iterator' => new \ArrayIterator([0 => ['module_name' => 'Coco_Module']]) + ] + ); + } + + public function testCurrent() + { + $this->moduleManagerMock->expects($this->once()) + ->method('isEnabled') + ->with('Coco_Module') + ->willReturn(true); + foreach ($this->moduleIterator as $item) { + $this->assertEquals(['module_name' => 'Coco_Module', 'status' => 'Enabled'], $item); + } + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/StoreConfigurationProviderTest.php b/app/code/Magento/Analytics/Test/Unit/Model/StoreConfigurationProviderTest.php new file mode 100644 index 0000000000000..13c76282b0b20 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/StoreConfigurationProviderTest.php @@ -0,0 +1,123 @@ +scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->websiteMock = $this->getMockBuilder(WebsiteInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->storeMock = $this->getMockBuilder(StoreInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configPaths = [ + 'web/unsecure/base_url', + 'currency/options/base', + 'general/locale/timezone' + ]; + + $this->storeConfigurationProvider = new StoreConfigurationProvider( + $this->scopeConfigMock, + $this->storeManagerMock, + $this->configPaths + ); + } + + public function testGetReport() + { + $map = [ + ['web/unsecure/base_url', 'default', 0, '127.0.0.1'], + ['currency/options/base', 'default', 0, 'USD'], + ['general/locale/timezone', 'default', 0, 'America/Dawson'], + ['web/unsecure/base_url', 'websites', 1, '127.0.0.2'], + ['currency/options/base', 'websites', 1, 'USD'], + ['general/locale/timezone', 'websites', 1, 'America/Belem'], + ['web/unsecure/base_url', 'stores', 2, '127.0.0.3'], + ['currency/options/base', 'stores', 2, 'USD'], + ['general/locale/timezone', 'stores', 2, 'America/Phoenix'], + ]; + + $this->scopeConfigMock + ->method('getValue') + ->will($this->returnValueMap($map)); + + $this->storeManagerMock->expects($this->once()) + ->method('getWebsites') + ->willReturn([$this->websiteMock]); + + $this->storeManagerMock->expects($this->once()) + ->method('getStores') + ->willReturn([$this->storeMock]); + + $this->websiteMock->expects($this->once()) + ->method('getId') + ->willReturn(1); + + $this->storeMock->expects($this->once()) + ->method('getId') + ->willReturn(2); + $result = iterator_to_array($this->storeConfigurationProvider->getReport()); + $resultValues = []; + foreach ($result as $item) { + $resultValues[] = array_values($item); + } + array_multisort($resultValues); + array_multisort($map); + $this->assertEquals($resultValues, $map); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/SubscriptionStatusProviderTest.php b/app/code/Magento/Analytics/Test/Unit/Model/SubscriptionStatusProviderTest.php new file mode 100644 index 0000000000000..4e32308d51e3c --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/SubscriptionStatusProviderTest.php @@ -0,0 +1,94 @@ +systemConfigMock = $this->getMockBuilder(System::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->analyticsTokenMock = $this->getMockBuilder(AnalyticsToken::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->statusProvider = $this->objectManagerHelper->getObject( + SubscriptionStatusProvider::class, + [ + 'systemConfig' => $this->systemConfigMock, + 'analyticsToken' => $this->analyticsTokenMock + ] + ); + } + + /** + * @dataProvider statusDataProvider + * + * @param bool $isSubscriptionEnabled + * @param bool $hasToken + * @param int $attempts + * @param string $expectedStatus + */ + public function testGetStatus($isSubscriptionEnabled, $hasToken, $attempts, $expectedStatus) + { + $this->analyticsTokenMock->expects($this->exactly($attempts)) + ->method('isTokenExist') + ->willReturn($hasToken); + $this->systemConfigMock->expects($this->once()) + ->method('get') + ->with('default/analytics/subscription/enabled') + ->willReturn($isSubscriptionEnabled); + $this->assertEquals($expectedStatus, $this->statusProvider->getStatus()); + } + + /** + * @return array + */ + public function statusDataProvider() + { + return [ + 'TestWithEnabledStatus' => [true, true, 1, "Enabled"], + 'TestWithPendingStatus' => [true, false, 1, "Pending"], + 'TestWithDisabledStatus' => [false, false, 0, "Disabled"], + 'TestWithDisabledStatus2' => [false, true, 0, "Disabled"], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Model/SubscriptionTest.php b/app/code/Magento/Analytics/Test/Unit/Model/SubscriptionTest.php new file mode 100644 index 0000000000000..693b97cb0aff2 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Model/SubscriptionTest.php @@ -0,0 +1,197 @@ +configValueFactoryMock = $this->getMockBuilder(ValueFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configValueMock = $this->getMockBuilder(Value::class) + ->disableOriginalConstructor() + ->setMethods(['setValue', 'setPath']) + ->getMock(); + + $this->configStructureMock = $this->getMockBuilder(SearchInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configValueResourceMock = $this->getMockBuilder(AbstractDb::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->elementFieldMock = $this->getMockBuilder(Field::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->reinitableConfigMock = $this->getMockBuilder(ReinitableConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->subscriptionModel = $this->objectManagerHelper->getObject( + SubscriptionModel::class, + [ + 'configValueFactory' => $this->configValueFactoryMock, + 'configStructure' => $this->configStructureMock, + 'configValueResource' => $this->configValueResourceMock, + 'reinitableConfig' => $this->reinitableConfigMock, + 'enabledConfigStructurePath' => $this->enableConfigStructurePath, + 'yesValueDropdown' => $this->yesValueDropdown, + ] + ); + } + + /** + * @dataProvider enabledDataProvider + * + * @param boolean $backendModel + * @param string $configPath + * + * @return void + */ + public function testEnabled($backendModel, $configPath) + { + $this->configStructureMock + ->expects($this->once()) + ->method('getElement') + ->with($this->enableConfigStructurePath) + ->willReturn($this->elementFieldMock); + $this->elementFieldMock + ->expects($this->once()) + ->method('hasBackendModel') + ->willReturn($backendModel); + if ($backendModel) { + $this->elementFieldMock + ->expects($this->once()) + ->method('getBackendModel') + ->willReturn($this->configValueMock); + } else { + $this->configValueFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->configValueMock); + } + $this->elementFieldMock + ->expects($this->once()) + ->method('getConfigPath') + ->willReturn($configPath); + $configPath = $configPath ?: $this->enableConfigStructurePath; + $this->configValueResourceMock + ->expects($this->once()) + ->method('load') + ->with($this->configValueMock, $configPath, 'path') + ->willReturnSelf(); + $this->configValueMock + ->expects($this->once()) + ->method('setValue') + ->with(1) + ->willReturnSelf(); + $this->configValueMock + ->expects($this->once()) + ->method('setPath') + ->with($configPath) + ->willReturnSelf(); + $this->configValueResourceMock + ->expects($this->once()) + ->method('save') + ->with($this->configValueMock) + ->willReturnSelf(); + $this->reinitableConfigMock + ->expects($this->once()) + ->method('reinit') + ->willReturnSelf(); + $this->assertTrue($this->subscriptionModel->enable()); + } + + /** + * @return array + */ + public function enabledDataProvider() + { + return [ + 'TestWithBackendModelWithoutConfigPath' => [true, null], + 'TestWithBackendModelWithConfigPath' => [true, $this->configPath], + 'TestWithoutBackendModelWithoutConfigPath' => [false, null], + 'TestWithoutBackendModelWithConfigPath' => [false, $this->configPath], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/Converter/XmlTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/Converter/XmlTest.php new file mode 100644 index 0000000000000..57e2514259c1c --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/Converter/XmlTest.php @@ -0,0 +1,121 @@ +objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\ReportXml\Config\Converter\Xml::class + ); + } + + /** + * @return void + */ + public function testConvertNoElements() + { + $this->assertEmpty( + $this->subject->convert(new \DOMDocument()) + ); + } + + /** + * @return void + */ + public function testConvert() + { + $dom = new \DOMDocument(); + + $expectedArray = [ + 'config' => [ + [ + 'noNamespaceSchemaLocation' => 'urn:magento:module:Magento_Analytics:etc/reports.xsd', + 'report' => [ + [ + 'name' => 'test_report_1', + 'connection' => 'sales', + 'source' => [ + [ + 'name' => 'sales_order', + 'alias' => 'orders', + 'attribute' => [ + [ + 'name' => 'entity_id', + 'alias' => 'identifier', + ] + ], + 'filter' => [ + [ + 'glue' => 'and', + 'condition' => [ + [ + 'attribute' => 'entity_id', + 'operator' => 'gt', + '_value' => '10' + ] + ] + ] + ] + ] + ] + ], + [ + 'name' => 'test_report_2', + 'connection' => 'default', + 'source' => [ + [ + 'name' => 'customer_entity', + 'alias' => 'customers', + 'attribute' => [ + [ + 'name' => 'email' + ] + ], + 'filter' => [ + [ + 'glue' => 'and', + 'condition' => [ + [ + 'attribute' => 'dob', + 'operator' => 'null' + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ]; + + $dom->loadXML(file_get_contents(__DIR__ . '/../_files/valid_reports.xml')); + + $this->assertEquals($expectedArray, $this->subject->convert($dom)); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/MapperTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/MapperTest.php new file mode 100644 index 0000000000000..b92ea2e866f74 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/MapperTest.php @@ -0,0 +1,47 @@ +mapper = new Mapper(); + } + + public function testExecute() + { + $configData['config'][0]['report'] = [ + [ + 'source' => ['product'], + 'name' => 'Product', + ] + ]; + $expectedResult = [ + 'Product' => [ + 'source' => 'product', + 'name' => 'Product', + ] + ]; + $this->assertEquals($this->mapper->execute($configData), $expectedResult); + } + + public function testExecuteWithoutReports() + { + $configData = []; + $this->assertEquals($this->mapper->execute($configData), []); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/SchemaLocatorTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/SchemaLocatorTest.php new file mode 100644 index 0000000000000..a4b14490a6fba --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/SchemaLocatorTest.php @@ -0,0 +1,74 @@ +urnResolverMock = $this->getMockBuilder(UrnResolver::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->schemaLocator = $this->objectManagerHelper->getObject( + SchemaLocator::class, + [ + 'urnResolver' => $this->urnResolverMock, + 'realPath' => $this->examplePath, + ] + ); + } + + public function testGetSchema() + { + $schema = 'schema'; + + $this->urnResolverMock + ->expects($this->once()) + ->method('getRealPath') + ->with($this->examplePath) + ->willReturn($schema); + + $this->assertSame($schema, $this->schemaLocator->getSchema()); + } + + public function testGetPerFileSchema() + { + $this->assertNull($this->schemaLocator->getPerFileSchema()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/_files/valid_reports.xml b/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/_files/valid_reports.xml new file mode 100644 index 0000000000000..b1103893a25cf --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/Config/_files/valid_reports.xml @@ -0,0 +1,25 @@ + + + + + + + + 10 + + + + + + + + + + + + diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/ConfigTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/ConfigTest.php new file mode 100644 index 0000000000000..21c4e6dd08019 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/ConfigTest.php @@ -0,0 +1,64 @@ +dataMock = $this->getMockBuilder(Data::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->config = $this->objectManagerHelper->getObject( + Config::class, + [ + 'data' => $this->dataMock, + ] + ); + } + + public function testGet() + { + $queryName = 'query string'; + $queryResult = [ 'query' => 1 ]; + + $this->dataMock + ->expects($this->once()) + ->method('get') + ->with($queryName) + ->willReturn($queryResult); + + $this->assertSame($queryResult, $this->config->get($queryName)); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/ConnectionFactoryTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/ConnectionFactoryTest.php new file mode 100644 index 0000000000000..9c88eb7616e30 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/ConnectionFactoryTest.php @@ -0,0 +1,106 @@ +resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->connectionMock = $this->getMockBuilder(MysqlPdoAdapter::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->connectionNewMock = $this->getMockBuilder(MysqlPdoAdapter::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->connectionFactory = $this->objectManagerHelper->getObject( + ConnectionFactory::class, + [ + 'resourceConnection' => $this->resourceConnectionMock, + 'objectManager' => $this->objectManagerMock, + ] + ); + } + + public function testGetConnection() + { + $connectionName = 'read'; + + $this->resourceConnectionMock + ->expects($this->once()) + ->method('getConnection') + ->with($connectionName) + ->willReturn($this->connectionMock); + + $this->connectionMock + ->expects($this->once()) + ->method('getConfig') + ->with() + ->willReturn(['persistent' => 1]); + + $this->objectManagerMock + ->expects($this->once()) + ->method('create') + ->with(get_class($this->connectionMock), ['config' => ['use_buffered_query' => false]]) + ->willReturn($this->connectionNewMock); + + $this->assertSame($this->connectionNewMock, $this->connectionFactory->getConnection($connectionName)); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/FilterAssemblerTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/FilterAssemblerTest.php new file mode 100644 index 0000000000000..4266b7602e009 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/FilterAssemblerTest.php @@ -0,0 +1,143 @@ +nameResolverMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\NameResolver::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectBuilderMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\SelectBuilder::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->selectBuilderMock->expects($this->any()) + ->method('getFilters') + ->willReturn([]); + + $this->conditionResolverMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\ConditionResolver::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\ReportXml\DB\Assembler\FilterAssembler::class, + [ + 'conditionResolver' => $this->conditionResolverMock, + 'nameResolver' => $this->nameResolverMock + ] + ); + } + + /** + * @return void + */ + public function testAssembleEmpty() + { + $queryConfigMock = [ + 'source' => [ + 'name' => 'sales_order', + 'alias' => 'sales' + ] + ]; + + $this->selectBuilderMock->expects($this->never()) + ->method('setFilters'); + + $this->assertEquals( + $this->selectBuilderMock, + $this->subject->assemble($this->selectBuilderMock, $queryConfigMock) + ); + } + + /** + * @return void + */ + public function testAssembleNotEmpty() + { + $queryConfigMock = [ + 'source' => [ + 'name' => 'sales_order', + 'alias' => 'sales', + 'filter' => [ + [ + 'glue' => 'and', + 'condition' => [ + [ + 'attribute' => 'entity_id', + 'operator' => 'null' + ] + ] + ] + ] + ] + ]; + + $this->nameResolverMock->expects($this->any()) + ->method('getAlias') + ->with($queryConfigMock['source']) + ->willReturn($queryConfigMock['source']['alias']); + + $this->conditionResolverMock->expects($this->once()) + ->method('getFilter') + ->with( + $this->selectBuilderMock, + $queryConfigMock['source']['filter'], + $queryConfigMock['source']['alias'] + ) + ->willReturn('(sales.entity_id IS NULL)'); + + $this->selectBuilderMock->expects($this->once()) + ->method('setFilters') + ->with(['(sales.entity_id IS NULL)']); + + $this->assertEquals( + $this->selectBuilderMock, + $this->subject->assemble($this->selectBuilderMock, $queryConfigMock) + ); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/FromAssemblerTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/FromAssemblerTest.php new file mode 100644 index 0000000000000..e5e847648039d --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/FromAssemblerTest.php @@ -0,0 +1,121 @@ +nameResolverMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\NameResolver::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectBuilderMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\SelectBuilder::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->selectBuilderMock->expects($this->any()) + ->method('getColumns') + ->willReturn([]); + + $this->columnsResolverMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\ColumnsResolver::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\ReportXml\DB\Assembler\FromAssembler::class, + [ + 'nameResolver' => $this->nameResolverMock, + 'columnsResolver' => $this->columnsResolverMock + ] + ); + } + + /** + * @return void + */ + public function testAssemble() + { + $queryConfigMock = [ + 'source' => [ + 'name' => 'sales_order', + 'alias' => 'sales', + 'attribute' => [ + [ + 'name' => 'entity_id' + ] + ] + ] + ]; + + $this->nameResolverMock->expects($this->any()) + ->method('getAlias') + ->with($queryConfigMock['source']) + ->willReturn($queryConfigMock['source']['alias']); + + $this->nameResolverMock->expects($this->any()) + ->method('getName') + ->with($queryConfigMock['source']) + ->willReturn($queryConfigMock['source']['name']); + + $this->selectBuilderMock->expects($this->once()) + ->method('setFrom') + ->with([$queryConfigMock['source']['alias'] => $queryConfigMock['source']['name']]); + + $this->columnsResolverMock->expects($this->once()) + ->method('getColumns') + ->with($this->selectBuilderMock, $queryConfigMock['source']) + ->willReturn(['entity_id' => 'sales.entity_id']); + + $this->selectBuilderMock->expects($this->once()) + ->method('setColumns') + ->with(['entity_id' => 'sales.entity_id']); + + $this->assertEquals( + $this->selectBuilderMock, + $this->subject->assemble($this->selectBuilderMock, $queryConfigMock) + ); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/JoinAssemblerTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/JoinAssemblerTest.php new file mode 100644 index 0000000000000..f32f9dc3fa152 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/Assembler/JoinAssemblerTest.php @@ -0,0 +1,261 @@ +nameResolverMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\NameResolver::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectBuilderMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\SelectBuilder::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->selectBuilderMock->expects($this->any()) + ->method('getFilters') + ->willReturn([]); + $this->selectBuilderMock->expects($this->any()) + ->method('getColumns') + ->willReturn([]); + $this->selectBuilderMock->expects($this->any()) + ->method('getJoins') + ->willReturn([]); + + $this->columnsResolverMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\ColumnsResolver::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->conditionResolverMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\ConditionResolver::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\ReportXml\DB\Assembler\JoinAssembler::class, + [ + 'conditionResolver' => $this->conditionResolverMock, + 'nameResolver' => $this->nameResolverMock, + 'columnsResolver' => $this->columnsResolverMock + ] + ); + } + + /** + * @return void + */ + public function testAssembleEmpty() + { + $queryConfigMock = [ + 'source' => [ + 'name' => 'sales_order', + 'alias' => 'sales' + ] + ]; + + $this->selectBuilderMock->expects($this->never()) + ->method('setColumns'); + $this->selectBuilderMock->expects($this->never()) + ->method('setFilters'); + $this->selectBuilderMock->expects($this->never()) + ->method('setJoins'); + + $this->assertEquals( + $this->selectBuilderMock, + $this->subject->assemble($this->selectBuilderMock, $queryConfigMock) + ); + } + + /** + * @param array $queryConfigMock + * + * @dataProvider assembleNotEmptyDataProvider + * @return void + */ + public function testAssembleNotEmpty(array $queryConfigMock) + { + $filtersMock = []; + + $joinsMock = [ + 'billing' => [ + 'link-type' => 'left', + 'table' => [ + 'billing' => 'sales_order_address' + ], + 'condition' => '(billing.parent_id = `sales`.`entity_id`)' + ] + ]; + + $this->nameResolverMock->expects($this->at(0)) + ->method('getAlias') + ->with($queryConfigMock['source']) + ->willReturn($queryConfigMock['source']['alias']); + $this->nameResolverMock->expects($this->at(1)) + ->method('getAlias') + ->with($queryConfigMock['source']['link-source'][0]) + ->willReturn($queryConfigMock['source']['link-source'][0]['alias']); + $this->nameResolverMock->expects($this->any()) + ->method('getName') + ->with($queryConfigMock['source']['link-source'][0]) + ->willReturn($queryConfigMock['source']['link-source'][0]['name']); + + $this->conditionResolverMock->expects($this->at(0)) + ->method('getFilter') + ->with( + $this->selectBuilderMock, + $queryConfigMock['source']['link-source'][0]['using'], + $queryConfigMock['source']['link-source'][0]['alias'], + $queryConfigMock['source']['alias'] + ) + ->willReturn('(billing.parent_id = `sales`.`entity_id`)'); + + if (isset($queryConfigMock['source']['link-source'][0]['filter'])) { + $filtersMock = ['(sales.entity_id IS NULL)']; + + $this->conditionResolverMock->expects($this->at(1)) + ->method('getFilter') + ->with( + $this->selectBuilderMock, + $queryConfigMock['source']['link-source'][0]['filter'], + $queryConfigMock['source']['link-source'][0]['alias'], + $queryConfigMock['source']['alias'] + ) + ->willReturn($filtersMock[0]); + + $this->columnsResolverMock->expects($this->once()) + ->method('getColumns') + ->with($this->selectBuilderMock, $queryConfigMock['source']['link-source'][0]) + ->willReturn( + [ + 'entity_id' => 'sales.entity_id', + 'billing_address_id' => 'billing.entity_id' + ] + ); + + $this->selectBuilderMock->expects($this->once()) + ->method('setColumns') + ->with( + [ + 'entity_id' => 'sales.entity_id', + 'billing_address_id' => 'billing.entity_id' + ] + ); + } + + $this->selectBuilderMock->expects($this->once()) + ->method('setFilters') + ->with($filtersMock); + $this->selectBuilderMock->expects($this->once()) + ->method('setJoins') + ->with($joinsMock); + + $this->assertEquals( + $this->selectBuilderMock, + $this->subject->assemble($this->selectBuilderMock, $queryConfigMock) + ); + } + + /** + * @return array + */ + public function assembleNotEmptyDataProvider() + { + return [ + [ + [ + 'source' => [ + 'name' => 'sales_order', + 'alias' => 'sales', + 'link-source' => [ + [ + 'name' => 'sales_order_address', + 'alias' => 'billing', + 'link-type' => 'left', + 'attribute' => [ + [ + 'alias' => 'billing_address_id', + 'name' => 'entity_id' + ] + ], + 'using' => [ + [ + 'glue' => 'and', + 'condition' => [ + [ + 'attribute' => 'parent_id', + 'operator' => 'eq', + 'type' => 'identifier', + '_value' => 'entity_id' + ] + ] + ] + ], + 'filter' => [ + [ + 'glue' => 'and', + 'condition' => [ + [ + 'attribute' => 'entity_id', + 'operator' => 'null' + ] + ] + ] + ] + ] + ] + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ColumnsResolverTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ColumnsResolverTest.php new file mode 100644 index 0000000000000..ae79f7536c504 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ColumnsResolverTest.php @@ -0,0 +1,108 @@ +nameResolverMock = $this->getMockBuilder(NameResolver::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectBuilderMock = $this->getMockBuilder(SelectBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->columnsResolver = new ColumnsResolver($this->nameResolverMock); + } + + public function testGetColumnsWithoutAttributes() + { + $this->assertEquals($this->columnsResolver->getColumns($this->selectBuilderMock, []), []); + } + + /** + * @dataProvider dataProvider + */ + public function testGetColumns($expression, $attributeData) + { + $columnAlias = 'fn'; + $expr = new \Zend_Db_Expr($expression); + $expectedResult = [$columnAlias => $expr]; + $columns = [$columnAlias => 'name']; + $entityConfig['attribute'] = ['attribute1' => $attributeData]; + $this->selectBuilderMock->expects($this->once()) + ->method('getColumns') + ->willReturn($columns); + $this->nameResolverMock->expects($this->at(0)) + ->method('getAlias') + ->with($attributeData) + ->willReturn($columnAlias); + $this->nameResolverMock->expects($this->at(1)) + ->method('getAlias') + ->with($entityConfig) + ->willReturn($columnAlias); + $this->nameResolverMock->expects($this->once()) + ->method('getName') + ->with($attributeData) + ->willReturn('name'); + $group = ['g']; + $this->selectBuilderMock->expects($this->once()) + ->method('getGroup') + ->willReturn($group); + $this->selectBuilderMock->expects($this->once()) + ->method('setGroup') + ->with(array_merge($group, $expectedResult)); + $this->assertEquals( + $this->columnsResolver->getColumns( + $this->selectBuilderMock, + $entityConfig + ), + $expectedResult + ); + } + + public function dataProvider() + { + return [ + 'TestWithFunction' => + [ + 'expression' => "SUM( DISTINCT fn.name)", + 'attributeData' => ['adata1', 'function' => 'SUM', 'distinct' => true, 'group' => true], + ], + 'TestWithoutFunction' => [ + 'expression' => "fn.name", + 'attributeData' => ['adata1', 'distinct' => true, 'group' => true], + ], + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ConditionResolverTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ConditionResolverTest.php new file mode 100644 index 0000000000000..1edf6a79ffa14 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ConditionResolverTest.php @@ -0,0 +1,102 @@ +resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectBuilderMock = $this->getMockBuilder(SelectBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->connectionMock = $this->getMockBuilder(AdapterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->conditionResolver = new ConditionResolver($this->resourceConnectionMock); + } + + public function testGetFilter() + { + $condition = ["type" => "variable", "_value" => "1", "attribute" => "id", "operator" => "neq"]; + $valueCondition = ["type" => "value", "_value" => "2", "attribute" => "first_name", "operator" => "eq"]; + $identifierCondition = [ + "type" => "identifier", + "_value" => "3", + "attribute" => "last_name", + "operator" => "eq"]; + $filter = [["glue" => "AND", "condition" => [$valueCondition]]]; + $filterConfig = [ + ["glue" => "OR", "condition" => [$condition], 'filter' => $filter], + ["glue" => "OR", "condition" => [$identifierCondition]], + ]; + $aliasName = 'n'; + $this->selectBuilderMock->expects($this->any()) + ->method('setParams') + ->with(array_merge([], [$condition['_value']])); + + $this->selectBuilderMock->expects($this->once()) + ->method('getParams') + ->willReturn([]); + + $this->selectBuilderMock->expects($this->any()) + ->method('getColumns') + ->willReturn(['price' => new \Zend_Db_Expr("(n.price = 400)")]); + + $this->resourceConnectionMock->expects($this->once()) + ->method('getConnection') + ->willReturn($this->connectionMock); + + $this->connectionMock->expects($this->any()) + ->method('quote') + ->willReturn("'John'"); + + $this->connectionMock->expects($this->once()) + ->method('quoteIdentifier') + ->willReturn("'Smith'"); + $result = "(n.id != 1 OR ((n.first_name = 'John'))) AND (n.last_name = 'Smith')"; + $this->assertEquals( + $this->conditionResolver->getFilter($this->selectBuilderMock, $filterConfig, $aliasName), + $result + ); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/NameResolverTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/NameResolverTest.php new file mode 100644 index 0000000000000..9bcbd1dadbd87 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/NameResolverTest.php @@ -0,0 +1,90 @@ +nameResolverMock = $this->getMockBuilder(NameResolver::class) + ->disableOriginalConstructor() + ->setMethods(['getName']) + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->nameResolver = $this->objectManagerHelper->getObject(NameResolver::class); + } + + public function testGetName() + { + $elementConfigMock = [ + 'name' => 'sales_order', + 'alias' => 'sales', + ]; + + $this->assertSame('sales_order', $this->nameResolver->getName($elementConfigMock)); + } + + /** + * @param array $elementConfig + * @param string|null $elementAlias + * + * @dataProvider getAliasDataProvider + */ + public function testGetAlias($elementConfig, $elementAlias) + { + $elementName = 'elementName'; + + $this->nameResolverMock + ->expects($this->once()) + ->method('getName') + ->with($elementConfig) + ->willReturn($elementName); + + $this->assertSame($elementAlias ?: $elementName, $this->nameResolverMock->getAlias($elementConfig)); + } + + /** + * @return array + */ + public function getAliasDataProvider() + { + return [ + 'ElementConfigWithAliases' => [ + ['alias' => 'sales', 'name' => 'sales_order'], + 'sales', + ], + 'ElementConfigWithoutAliases' => [ + ['name' => 'sales_order'], + null, + ] + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ReportValidatorTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ReportValidatorTest.php new file mode 100644 index 0000000000000..ae64b28cd97a0 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/ReportValidatorTest.php @@ -0,0 +1,125 @@ +connectionFactoryMock = $this->getMockBuilder(ConnectionFactory::class) + ->disableOriginalConstructor()->getMock(); + $this->queryFactoryMock = $this->getMockBuilder(QueryFactory::class) + ->disableOriginalConstructor()->getMock(); + $this->queryMock = $this->getMockBuilder(Query::class)->disableOriginalConstructor() + ->getMock(); + $this->connectionMock = $this->getMockBuilder(AdapterInterface::class)->getMockForAbstractClass(); + $this->selectMock = $this->getMockBuilder(Select::class)->disableOriginalConstructor() + ->getMock(); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->reportValidator = $this->objectManagerHelper->getObject( + ReportValidator::class, + [ + 'connectionFactory' => $this->connectionFactoryMock, + 'queryFactory' => $this->queryFactoryMock + ] + ); + } + + /** + * @dataProvider errorDataProvider + * @param string $reportName + * @param array $result + * @param \PHPUnit_Framework_MockObject_Stub $queryReturnStub + */ + public function testValidate($reportName, $result, \PHPUnit_Framework_MockObject_Stub $queryReturnStub) + { + $connectionName = 'testConnection'; + $this->queryFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->queryMock); + $this->queryMock->expects($this->once())->method('getConnectionName')->willReturn($connectionName); + $this->connectionFactoryMock->expects($this->once())->method('getConnection') + ->with($connectionName) + ->willReturn($this->connectionMock); + $this->queryMock->expects($this->atLeastOnce())->method('getSelect')->willReturn($this->selectMock); + $this->selectMock->expects($this->once())->method('limit')->with(0); + $this->connectionMock->expects($this->once())->method('query')->with($this->selectMock)->will($queryReturnStub); + $this->assertEquals($result, $this->reportValidator->validate($reportName)); + } + + /** + * Provide variations of the error returning + * + * @return array + */ + public function errorDataProvider() + { + $reportName = 'test'; + $errorMessage = 'SQL Error 42'; + return [ + [ + $reportName, + 'expectedResult' => [], + 'queryReturnStub' => $this->returnValue(null) + ], + [ + $reportName, + 'expectedResult' => [$reportName, $errorMessage], + 'queryReturnStub' => $this->throwException(new \Zend_Db_Statement_Exception($errorMessage)) + ] + ]; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/SelectBuilderTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/SelectBuilderTest.php new file mode 100644 index 0000000000000..b070000ea9f9d --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/DB/SelectBuilderTest.php @@ -0,0 +1,103 @@ +resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->connectionMock = $this->getMockBuilder(AdapterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectMock = $this->getMockBuilder(Select::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectBuilder = new SelectBuilder($this->resourceConnectionMock); + } + + public function testCreate() + { + $connectionName = 'MySql'; + $from = ['customer c']; + $columns = ['id', 'name', 'price']; + $filter = 'filter'; + $joins = [ + ['link-type' => 'left', 'table' => 'customer', 'condition' => 'in'], + ['link-type' => 'inner', 'table' => 'price', 'condition' => 'eq'], + ['link-type' => 'right', 'table' => 'attribute', 'condition' => 'neq'], + ]; + $groups = ['id', 'name']; + $this->selectBuilder->setConnectionName($connectionName); + $this->selectBuilder->setFrom($from); + $this->selectBuilder->setColumns($columns); + $this->selectBuilder->setFilters([$filter]); + $this->selectBuilder->setJoins($joins); + $this->selectBuilder->setGroup($groups); + $this->resourceConnectionMock->expects($this->once()) + ->method('getConnection') + ->with($connectionName) + ->willReturn($this->connectionMock); + $this->connectionMock->expects($this->once()) + ->method('select') + ->willReturn($this->selectMock); + $this->selectMock->expects($this->once()) + ->method('from') + ->with($from, []); + $this->selectMock->expects($this->once()) + ->method('columns') + ->with($columns); + $this->selectMock->expects($this->once()) + ->method('where') + ->with($filter); + $this->selectMock->expects($this->once()) + ->method('joinLeft') + ->with($joins[0]['table'], $joins[0]['condition'], []); + $this->selectMock->expects($this->once()) + ->method('joinInner') + ->with($joins[1]['table'], $joins[1]['condition'], []); + $this->selectMock->expects($this->once()) + ->method('joinRight') + ->with($joins[2]['table'], $joins[2]['condition'], []); + $this->selectBuilder->create(); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/IteratorFactoryTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/IteratorFactoryTest.php new file mode 100644 index 0000000000000..dee374b3bae05 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/IteratorFactoryTest.php @@ -0,0 +1,59 @@ +objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->iteratorIteratorMock = $this->getMockBuilder(\IteratorIterator::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->iteratorFactory = new IteratorFactory( + $this->objectManagerMock + ); + } + + public function testCreate() + { + $arrayObject = new \ArrayIterator([1, 2, 3, 4, 5]); + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with(\IteratorIterator::class, ['iterator' => $arrayObject]) + ->willReturn($this->iteratorIteratorMock); + + $this->assertEquals($this->iteratorFactory->create($arrayObject), $this->iteratorIteratorMock); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryFactoryTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryFactoryTest.php new file mode 100644 index 0000000000000..1b8502ad0fc53 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryFactoryTest.php @@ -0,0 +1,239 @@ +queryMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\Query::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->configMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\Config::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectMock = $this->getMockBuilder( + \Magento\Framework\DB\Select::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->assemblerMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\Assembler\AssemblerInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->queryCacheMock = $this->getMockBuilder( + \Magento\Framework\App\CacheInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerMock = $this->getMockBuilder( + \Magento\Framework\ObjectManagerInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectHydratorMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\SelectHydrator::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectBuilderFactoryMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\SelectBuilderFactory::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\ReportXml\QueryFactory::class, + [ + 'config' => $this->configMock, + 'selectBuilderFactory' => $this->selectBuilderFactoryMock, + 'assemblers' => [$this->assemblerMock], + 'queryCache' => $this->queryCacheMock, + 'objectManager' => $this->objectManagerMock, + 'selectHydrator' => $this->selectHydratorMock + ] + ); + } + + /** + * @return void + */ + public function testCreateCached() + { + $queryName = 'test_query'; + + $this->queryCacheMock->expects($this->any()) + ->method('load') + ->with($queryName) + ->willReturn('{"connectionName":"sales","config":{},"select_parts":{}}'); + + $this->selectHydratorMock->expects($this->any()) + ->method('recreate') + ->with([]) + ->willReturn($this->selectMock); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with( + \Magento\Analytics\ReportXml\Query::class, + [ + 'select' => $this->selectMock, + 'selectHydrator' => $this->selectHydratorMock, + 'connectionName' => 'sales', + 'config' => [] + ] + ) + ->willReturn($this->queryMock); + + $this->queryCacheMock->expects($this->never()) + ->method('save'); + + $this->assertEquals( + $this->queryMock, + $this->subject->create($queryName) + ); + } + + /** + * @return void + */ + public function testCreateNotCached() + { + $queryName = 'test_query'; + + $queryConfigMock = [ + 'name' => 'test_query', + 'connection' => 'sales' + ]; + + $selectBuilderMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\DB\SelectBuilder::class + ) + ->disableOriginalConstructor() + ->getMock(); + $selectBuilderMock->expects($this->once()) + ->method('setConnectionName') + ->with($queryConfigMock['connection']); + $selectBuilderMock->expects($this->any()) + ->method('create') + ->willReturn($this->selectMock); + $selectBuilderMock->expects($this->any()) + ->method('getConnectionName') + ->willReturn($queryConfigMock['connection']); + + $this->queryCacheMock->expects($this->any()) + ->method('load') + ->with($queryName) + ->willReturn(null); + + $this->configMock->expects($this->any()) + ->method('get') + ->with($queryName) + ->willReturn($queryConfigMock); + + $this->selectBuilderFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($selectBuilderMock); + + $this->assemblerMock->expects($this->once()) + ->method('assemble') + ->with($selectBuilderMock, $queryConfigMock) + ->willReturn($selectBuilderMock); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with( + \Magento\Analytics\ReportXml\Query::class, + [ + 'select' => $this->selectMock, + 'selectHydrator' => $this->selectHydratorMock, + 'connectionName' => $queryConfigMock['connection'], + 'config' => $queryConfigMock + ] + ) + ->willReturn($this->queryMock); + + $this->queryCacheMock->expects($this->once()) + ->method('save') + ->with(json_encode($this->queryMock), $queryName); + + $this->assertEquals( + $this->queryMock, + $this->subject->create($queryName) + ); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryTest.php new file mode 100644 index 0000000000000..9b5a4bca74b2b --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/QueryTest.php @@ -0,0 +1,90 @@ +selectMock = $this->getMockBuilder(Select::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectHydratorMock = $this->getMockBuilder(selectHydrator::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->query = $this->objectManagerHelper->getObject( + Query::class, + [ + 'select' => $this->selectMock, + 'connectionName' => $this->connectionName, + 'selectHydrator' => $this->selectHydratorMock, + 'config' => [] + ] + ); + } + + /** + * @return void + */ + public function testJsonSerialize() + { + $selectParts = ['part' => 1]; + + $this->selectHydratorMock + ->expects($this->once()) + ->method('extract') + ->with($this->selectMock) + ->willReturn($selectParts); + + $expectedResult = [ + 'connectionName' => $this->connectionName, + 'select_parts' => $selectParts, + 'config' => [] + ]; + + $this->assertSame($expectedResult, $this->query->jsonSerialize()); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/ReportProviderTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/ReportProviderTest.php new file mode 100644 index 0000000000000..ee20a59074485 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/ReportProviderTest.php @@ -0,0 +1,180 @@ +selectMock = $this->getMockBuilder( + \Magento\Framework\DB\Select::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->queryMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\Query::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->queryMock->expects($this->any()) + ->method('getSelect') + ->willReturn($this->selectMock); + + $this->iteratorMock = $this->getMockBuilder( + \IteratorIterator::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->statementMock = $this->getMockBuilder( + \Magento\Framework\DB\Statement\Pdo\Mysql::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->statementMock->expects($this->any()) + ->method('getIterator') + ->willReturn($this->iteratorMock); + + $this->connectionMock = $this->getMockBuilder( + \Magento\Framework\DB\Adapter\AdapterInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->queryFactoryMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\QueryFactory::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->iteratorFactoryMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\IteratorFactory::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->iteratorMock = $this->getMockBuilder( + \IteratorIterator::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->objectManagerHelper = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->connectionFactoryMock = $this->getMockBuilder( + \Magento\Analytics\ReportXml\ConnectionFactory::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->subject = $this->objectManagerHelper->getObject( + \Magento\Analytics\ReportXml\ReportProvider::class, + [ + 'queryFactory' => $this->queryFactoryMock, + 'connectionFactory' => $this->connectionFactoryMock, + 'iteratorFactory' => $this->iteratorFactoryMock + ] + ); + } + + /** + * @return void + */ + public function testGetReport() + { + $reportName = 'test_report'; + $connectionName = 'sales'; + + $this->queryFactoryMock->expects($this->once()) + ->method('create') + ->with($reportName) + ->willReturn($this->queryMock); + + $this->connectionFactoryMock->expects($this->once()) + ->method('getConnection') + ->with($connectionName) + ->willReturn($this->connectionMock); + + $this->queryMock->expects($this->once()) + ->method('getConnectionName') + ->willReturn($connectionName); + + $this->queryMock->expects($this->once()) + ->method('getConfig') + ->willReturn( + [ + 'connection' => $connectionName + ] + ); + + $this->connectionMock->expects($this->once()) + ->method('query') + ->with($this->selectMock) + ->willReturn($this->statementMock); + + $this->iteratorFactoryMock->expects($this->once()) + ->method('create') + ->with($this->statementMock, null) + ->willReturn($this->iteratorMock); + $this->assertEquals($this->iteratorMock, $this->subject->getReport($reportName)); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/ReportXml/SelectHydratorTest.php b/app/code/Magento/Analytics/Test/Unit/ReportXml/SelectHydratorTest.php new file mode 100644 index 0000000000000..e6f6c5144d338 --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/ReportXml/SelectHydratorTest.php @@ -0,0 +1,113 @@ +resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->connectionMock = $this->getMockBuilder(AdapterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectMock = $this->getMockBuilder(Select::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->selectHydrator = new SelectHydrator($this->resourceConnectionMock); + } + + public function testExtract() + { + $selectParts = + [ + Select::DISTINCT, + Select::COLUMNS, + Select::UNION, + Select::FROM, + Select::WHERE, + Select::GROUP, + Select::HAVING, + Select::ORDER, + Select::LIMIT_COUNT, + Select::LIMIT_OFFSET, + Select::FOR_UPDATE + ]; + + $result = []; + foreach ($selectParts as $part) { + $result[$part] = "Part"; + } + $this->selectMock->expects($this->any()) + ->method('getPart') + ->willReturn("Part"); + $this->assertEquals($this->selectHydrator->extract($this->selectMock), $result); + } + + public function testRecreate() + { + $this->resourceConnectionMock->expects($this->once()) + ->method('getConnection') + ->willReturn($this->connectionMock); + $this->connectionMock->expects($this->once()) + ->method('select') + ->willReturn($this->selectMock); + $parts = + [ + Select::DISTINCT, + Select::COLUMNS, + Select::UNION, + Select::FROM, + Select::WHERE, + Select::GROUP, + Select::HAVING, + Select::ORDER, + Select::LIMIT_COUNT, + Select::LIMIT_OFFSET, + Select::FOR_UPDATE + ]; + $selectParts = []; + foreach ($parts as $key => $part) { + $this->selectMock->expects($this->at($key)) + ->method('setPart') + ->with($part, 'part' . $key); + $selectParts[$part] = 'part' . $key; + } + $this->selectHydrator->recreate($selectParts); + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Ui/DataProvider/DummyDataProviderTest.php b/app/code/Magento/Analytics/Test/Unit/Ui/DataProvider/DummyDataProviderTest.php new file mode 100644 index 0000000000000..8e0cd06ef272b --- /dev/null +++ b/app/code/Magento/Analytics/Test/Unit/Ui/DataProvider/DummyDataProviderTest.php @@ -0,0 +1,228 @@ + 'value']; + + /** + * @return void + */ + protected function setUp() + { + $this->searchResultMock = $this->getMockBuilder(SearchResultInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->searchCriteriaMock = $this->getMockBuilder(SearchCriteriaInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->dataCollectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->filterMock = $this->getMockBuilder(Filter::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->dummyDataProvider = $this->objectManagerHelper->getObject( + DummyDataProvider::class, + [ + 'name' => $this->providerName, + 'searchResult' => $this->searchResultMock, + 'searchCriteria' => $this->searchCriteriaMock, + 'collection' => $this->dataCollectionMock, + 'data' => ['config' => $this->configData], + ] + ); + } + + /** + * @return void + */ + public function testGetName() + { + $this->assertSame($this->providerName, $this->dummyDataProvider->getName()); + } + + /** + * @return void + */ + public function testGetConfigData() + { + $this->assertSame($this->configData, $this->dummyDataProvider->getConfigData()); + $dataProvider = $this->objectManagerHelper + ->getObject( + DummyDataProvider::class, + [] + ); + $this->assertSame([], $dataProvider->getConfigData()); + } + + /** + * @return void + */ + public function testSetConfigData() + { + $configValue = ['key' => 'value']; + + $this->assertTrue($this->dummyDataProvider->setConfigData($configValue)); + $this->assertSame($configValue, $this->dummyDataProvider->getConfigData()); + } + + /** + * @return void + */ + public function testGetMeta() + { + $this->assertSame([], $this->dummyDataProvider->getMeta()); + } + + /** + * @return void + */ + public function testGetFieldMetaInfo() + { + $this->assertSame([], $this->dummyDataProvider->getFieldMetaInfo('', '')); + } + + /** + * @return void + */ + public function testGetFieldSetMetaInfo() + { + $this->assertSame([], $this->dummyDataProvider->getFieldSetMetaInfo('')); + } + + /** + * @return void + */ + public function testGetFieldsMetaInfo() + { + $this->assertSame([], $this->dummyDataProvider->getFieldsMetaInfo('')); + } + + /** + * @return void + */ + public function testGetPrimaryFieldName() + { + $this->assertSame('', $this->dummyDataProvider->getPrimaryFieldName()); + } + + /** + * @return void + */ + public function testGetRequestFieldName() + { + $this->assertSame('', $this->dummyDataProvider->getRequestFieldName()); + } + + /** + * @return void + */ + public function testGetData() + { + $this->dataCollectionMock + ->expects($this->once()) + ->method('toArray') + ->willReturn([]); + $this->assertSame([], $this->dummyDataProvider->getData()); + } + + /** + * @return void + */ + public function testAddFilter() + { + $this->assertNull($this->dummyDataProvider->addFilter($this->filterMock)); + } + + /** + * @return void + */ + public function testAddOrder() + { + $this->assertNull($this->dummyDataProvider->addOrder('', '')); + } + + /** + * @return void + */ + public function testSetLimit() + { + $this->assertNull($this->dummyDataProvider->setLimit(1, 1)); + } + + /** + * @return void + */ + public function testGetSearchCriteria() + { + $this->assertSame($this->searchCriteriaMock, $this->dummyDataProvider->getSearchCriteria()); + } + + /** + * @return void + */ + public function testGetSearchResult() + { + $this->assertSame($this->searchResultMock, $this->dummyDataProvider->getSearchResult()); + } +} diff --git a/app/code/Magento/Analytics/Ui/DataProvider/DummyDataProvider.php b/app/code/Magento/Analytics/Ui/DataProvider/DummyDataProvider.php new file mode 100644 index 0000000000000..e842b6572b8c2 --- /dev/null +++ b/app/code/Magento/Analytics/Ui/DataProvider/DummyDataProvider.php @@ -0,0 +1,237 @@ +name = $name; + $this->searchResult = $searchResult; + $this->searchCriteria = $searchCriteria; + $this->collection = $collection; + $this->data = $data; + } + + /** + * Get Data Provider name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get config data + * + * @return mixed + */ + public function getConfigData() + { + return isset($this->data['config']) ? $this->data['config'] : []; + } + + /** + * Set config data + * + * @param mixed $config + * + * @return bool + */ + public function setConfigData($config) + { + $this->data['config'] = $config; + + return true; + } + + /** + * @return array + */ + public function getMeta() + { + return []; + } + + /** + * @param string $fieldSetName + * @param string $fieldName + * + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getFieldMetaInfo($fieldSetName, $fieldName) + { + return []; + } + + /** + * Get field set meta info + * + * @param string $fieldSetName + * + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getFieldSetMetaInfo($fieldSetName) + { + return []; + } + + /** + * @param string $fieldSetName + * + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getFieldsMetaInfo($fieldSetName) + { + return []; + } + + /** + * Get primary field name + * + * @return string + */ + public function getPrimaryFieldName() + { + return ''; + } + + /** + * Get field name in request + * + * @return string + */ + public function getRequestFieldName() + { + return ''; + } + + /** + * Get data + * + * @return mixed + */ + public function getData() + { + return $this->collection->toArray(); + } + + /** + * Add field filter to collection + * + * @param \Magento\Framework\Api\Filter $filter + * + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function addFilter(\Magento\Framework\Api\Filter $filter) + { + } + + /** + * Add ORDER BY to the end or to the beginning + * + * @param string $field + * @param string $direction + * + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function addOrder($field, $direction) + { + } + + /** + * Set Query limit + * + * @param int $offset + * @param int $size + * + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function setLimit($offset, $size) + { + } + + /** + * Returns search criteria + * + * @return SearchCriteriaInterface + */ + public function getSearchCriteria() + { + return $this->searchCriteria; + } + + /** + * @return SearchResultInterface + */ + public function getSearchResult() + { + return $this->searchResult; + } +} diff --git a/app/code/Magento/Analytics/composer.json b/app/code/Magento/Analytics/composer.json new file mode 100644 index 0000000000000..b8d05bbd08a52 --- /dev/null +++ b/app/code/Magento/Analytics/composer.json @@ -0,0 +1,27 @@ +{ + "name": "magento/module-analytics", + "description": "N/A", + "require": { + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", + "magento/module-backend": "100.2.*", + "magento/module-admin-notification": "100.2.*", + "magento/module-config": "100.2.*", + "magento/module-integration": "100.2.*", + "magento/module-store": "100.2.*", + "magento/framework": "100.2.*" + }, + "type": "magento2-module", + "version": "100.2.0-dev", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\Analytics\\": "" + } + } +} diff --git a/app/code/Magento/Analytics/etc/acl.xml b/app/code/Magento/Analytics/etc/acl.xml new file mode 100644 index 0000000000000..9dffb87bbabc0 --- /dev/null +++ b/app/code/Magento/Analytics/etc/acl.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Analytics/etc/adminhtml/menu.xml b/app/code/Magento/Analytics/etc/adminhtml/menu.xml new file mode 100644 index 0000000000000..0d874688521da --- /dev/null +++ b/app/code/Magento/Analytics/etc/adminhtml/menu.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/app/code/Magento/Analytics/etc/adminhtml/routes.xml b/app/code/Magento/Analytics/etc/adminhtml/routes.xml new file mode 100644 index 0000000000000..232ebe94d314f --- /dev/null +++ b/app/code/Magento/Analytics/etc/adminhtml/routes.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..c3e15c0ba3c20 --- /dev/null +++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml @@ -0,0 +1,43 @@ + + + + +
    + + general + Magento_Analytics::analytics_settings + + + + + Magento\Config\Model\Config\Source\Yesno + Magento\Analytics\Model\Config\Backend\Enabled + analytics/subscription/enabled + + + Magento\Analytics\Block\Adminhtml\System\Config\SubscriptionStatusLabel + + + + Magento\Analytics\Model\Config\Source\Vertical + Magento\Analytics\Model\Config\Backend\Vertical + + + + + + Magento\Analytics\Model\Config\Backend\CollectionTime + + + 1 + + + +
    +
    +
    diff --git a/app/code/Magento/Analytics/etc/analytics.xml b/app/code/Magento/Analytics/etc/analytics.xml new file mode 100644 index 0000000000000..d35df54bffe7e --- /dev/null +++ b/app/code/Magento/Analytics/etc/analytics.xml @@ -0,0 +1,50 @@ + + + + + + + + modules + + + + + + + + + + + + + + stores + + + + + + + + + websites + + + + + + + + + groups + + + + + diff --git a/app/code/Magento/Analytics/etc/analytics.xsd b/app/code/Magento/Analytics/etc/analytics.xsd new file mode 100644 index 0000000000000..da3357f8eb75a --- /dev/null +++ b/app/code/Magento/Analytics/etc/analytics.xsd @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + File name attribute can has only [a-zA-Z0-9/_]. + + + + + + + + + + Value is required. + + + + + + + diff --git a/app/code/Magento/Analytics/etc/config.xml b/app/code/Magento/Analytics/etc/config.xml new file mode 100644 index 0000000000000..8a55adbdf730e --- /dev/null +++ b/app/code/Magento/Analytics/etc/config.xml @@ -0,0 +1,26 @@ + + + + + + + https://advancedreporting.rjmetrics.com/signup + https://advancedreporting.rjmetrics.com/update + https://dashboard.rjmetrics.com/v2/magento/signup + https://advancedreporting.rjmetrics.com/otp + https://advancedreporting.rjmetrics.com/report + + Magento Analytics user + + + 02,00,00 + + + + + diff --git a/app/code/Magento/Analytics/etc/crontab.xml b/app/code/Magento/Analytics/etc/crontab.xml new file mode 100644 index 0000000000000..ae74693df98ae --- /dev/null +++ b/app/code/Magento/Analytics/etc/crontab.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/code/Magento/Analytics/etc/di.xml b/app/code/Magento/Analytics/etc/di.xml new file mode 100644 index 0000000000000..4fe2693afcda8 --- /dev/null +++ b/app/code/Magento/Analytics/etc/di.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + Magento\Analytics\Model\Connector\SignUpCommand + Magento\Analytics\Model\Connector\UpdateCommand + + + + + + + Magento\Analytics\Model\Condition\CanViewNotification + + + + + + Magento\Config\Model\ResourceModel\Config\Data + + + + + + Magento\Analytics\ReportXml\Config\Reader\Xml + + + + + + + Magento\Analytics\ReportXml\DB\Assembler\FromAssembler + Magento\Analytics\ReportXml\DB\Assembler\FilterAssembler + Magento\Analytics\ReportXml\DB\Assembler\JoinAssembler + + + + + + + Magento\Analytics\Model\Config\Reader\Xml + + + + + + Magento\Analytics\Model\Config\Data + + + + + Magento\Analytics\Model\Config\SchemaLocator + Magento\Analytics\ReportXml\Config\Converter\Xml + + + + + Magento\Analytics\Model\Config\Reader + + + + + + web/unsecure/base_url + currency/options/base + general/locale/timezone + general/country/default + carriers/dhl/title + carriers/dhl/active + carriers/fedex/title + carriers/fedex/active + carriers/flatrate/title + carriers/flatrate/active + carriers/tablerate/title + carriers/tablerate/active + carriers/freeshipping/title + carriers/freeshipping/active + carriers/ups/title + carriers/ups/active + carriers/usps/title + carriers/usps/active + payment/free/title + payment/free/active + payment/checkmo/title + payment/checkmo/active + payment/purchaseorder/title + payment/purchaseorder/active + payment/banktransfer/title + payment/banktransfer/active + payment/cashondelivery/title + payment/cashondelivery/active + payment/authorizenet_directpost/title + payment/authorizenet_directpost/active + payment/paypal_billing_agreement/title + payment/paypal_billing_agreement/active + payment/braintree/title + payment/braintree/active + payment/braintree_paypal/title + payment/braintree_paypal/active + + + + + + + Apps and Games + Athletic/Sporting Goods + Art and Design + Auto Parts + Baby/Children’s Apparel, Gear and Toys + Beauty and Cosmetics + Books, Music and Magazines + Crafts and Stationery + Consumer Electronics + Deal Site + Fashion Apparel and Accessories + Food, Beverage and Grocery + Home Goods and Furniture + Home Improvement + Jewelry and Watches + Mass Merchant + Office Supplies + Outdoor and Camping Gear + Pet Goods + Pharma and Medical Devices + Technology B2B + Other + + + + + + + diff --git a/app/code/Magento/Analytics/etc/module.xml b/app/code/Magento/Analytics/etc/module.xml new file mode 100644 index 0000000000000..958d43cd6ee53 --- /dev/null +++ b/app/code/Magento/Analytics/etc/module.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/app/code/Magento/Analytics/etc/reports.xml b/app/code/Magento/Analytics/etc/reports.xml new file mode 100644 index 0000000000000..1c9f4a3d62700 --- /dev/null +++ b/app/code/Magento/Analytics/etc/reports.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/code/Magento/Analytics/etc/reports.xsd b/app/code/Magento/Analytics/etc/reports.xsd new file mode 100644 index 0000000000000..e7e341f5d9a36 --- /dev/null +++ b/app/code/Magento/Analytics/etc/reports.xsd @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Analytics/etc/webapi.xml b/app/code/Magento/Analytics/etc/webapi.xml new file mode 100644 index 0000000000000..401d09b06563d --- /dev/null +++ b/app/code/Magento/Analytics/etc/webapi.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/app/code/Magento/Analytics/registration.php b/app/code/Magento/Analytics/registration.php new file mode 100644 index 0000000000000..d07fe8ffb5e56 --- /dev/null +++ b/app/code/Magento/Analytics/registration.php @@ -0,0 +1,11 @@ + + + + + + + + + + + + diff --git a/app/code/Magento/Analytics/view/adminhtml/templates/dashboard/link.phtml b/app/code/Magento/Analytics/view/adminhtml/templates/dashboard/link.phtml new file mode 100644 index 0000000000000..6f7874b5c001e --- /dev/null +++ b/app/code/Magento/Analytics/view/adminhtml/templates/dashboard/link.phtml @@ -0,0 +1,13 @@ + + + escapeHtml(__('Free Tier')) ?> + diff --git a/app/code/Magento/Analytics/view/adminhtml/ui_component/analytics_subscription_form.xml b/app/code/Magento/Analytics/view/adminhtml/ui_component/analytics_subscription_form.xml new file mode 100644 index 0000000000000..60f3751d74bd4 --- /dev/null +++ b/app/code/Magento/Analytics/view/adminhtml/ui_component/analytics_subscription_form.xml @@ -0,0 +1,114 @@ + + +
    + + + analytics_subscription_form.analytics_subscription_form_data_source + analytics_subscription_form.analytics_subscription_form_data_source + + Analytics Subscription + + data + analytics_subscription_form + simple + true + + templates/form/collapsible + + + + + Magento\Analytics\Ui\DataProvider\DummyDataProvider + analytics_subscription_form_data_source + + + + + + false + + + + + + + Magento_Ui/js/form/provider + + + + + + + + Magento_Analytics/js/modal/modal-component + actionCancel + true + + Subscription Confirmation + popup + true + true + true + + + Cancel + action-secondary + + + ${ $.name } + actionCancel + + + + + Ok + action-primary + + + ${ $.parentName } + save + + + ${ $.name } + closeModal + + + + + + + + +
    + + + + + + + + + + admin__field-wide + I agree with sending my system + configuration and transaction data to Magento Analytics. + + checkbox + analytics_subscription_checkbox + + 1 + 0 + + 1 + + + +
    +
    +
    diff --git a/app/code/Magento/Analytics/view/adminhtml/web/js/modal/modal-component.js b/app/code/Magento/Analytics/view/adminhtml/web/js/modal/modal-component.js new file mode 100644 index 0000000000000..4b215db24857a --- /dev/null +++ b/app/code/Magento/Analytics/view/adminhtml/web/js/modal/modal-component.js @@ -0,0 +1,66 @@ +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'jquery', + 'Magento_Ui/js/modal/modal-component', + 'Magento_Ui/js/modal/alert', + 'mage/translate' +], function ($, Modal, alert, $t) { + 'use strict'; + + return Modal.extend({ + defaults: { + postponeOptions: {}, + imports: { + postponeUrl: '${ $.provider }:postpone_url' + }, + modules: { + form: '${ $.parentName }' + } + }, + + /** + * Send request to postpone modal appearance for a certain time. + * + * @param {Object} options - additional request options. + */ + sendPostponeRequest: function (options) { + var self = this, + data = $.extend(this.form().source.data, options); + + $.ajax({ + type: 'POST', + url: this.postponeUrl, + data: data, + showLoader: true + }).done(function (xhr) { + if (xhr.error) { + self.onError(xhr); + } + }).fail(this.onError); + }, + + /** + * Error handler. + * + * @param {Object} xhr - request result. + */ + onError: function (xhr) { + if (xhr.statusText === 'abort') { + return; + } + + alert({ + content: xhr.message || $t('An error occurred while subscription process.') + }); + }, + + /** @inheritdoc */ + actionCancel: function () { + this.sendPostponeRequest(this.postponeOptions); + this.closeModal(); + } + }); +}); diff --git a/app/code/Magento/Authorization/Model/Acl/AclRetriever.php b/app/code/Magento/Authorization/Model/Acl/AclRetriever.php index d3a5714c93743..5b67748cd2e41 100644 --- a/app/code/Magento/Authorization/Model/Acl/AclRetriever.php +++ b/app/code/Magento/Authorization/Model/Acl/AclRetriever.php @@ -1,6 +1,6 @@ _resource = $resource; $this->_groupFactory = $groupFactory; $this->_roleFactory = $roleFactory; + $this->aclDataCache = $aclDataCache ?: ObjectManager::getInstance()->get( + \Magento\Framework\Acl\Data\CacheInterface::class + ); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->cacheKey = $cacheKey; } /** @@ -49,12 +81,7 @@ public function __construct( */ public function populateAcl(\Magento\Framework\Acl $acl) { - $roleTableName = $this->_resource->getTableName('authorization_role'); - $connection = $this->_resource->getConnection(); - - $select = $connection->select()->from($roleTableName)->order('tree_level'); - - foreach ($connection->fetchAll($select) as $role) { + foreach ($this->getRolesArray() as $role) { $parent = $role['parent_id'] > 0 ? $role['parent_id'] : null; switch ($role['role_type']) { case RoleGroup::ROLE_TYPE: @@ -71,4 +98,28 @@ public function populateAcl(\Magento\Framework\Acl $acl) } } } + + /** + * Get application ACL roles array + * + * @return array + */ + private function getRolesArray() + { + $rolesCachedData = $this->aclDataCache->load($this->cacheKey); + if ($rolesCachedData) { + return $this->serializer->unserialize($rolesCachedData); + } + + $roleTableName = $this->_resource->getTableName('authorization_role'); + $connection = $this->_resource->getConnection(); + + $select = $connection->select() + ->from($roleTableName) + ->order('tree_level'); + + $rolesArray = $connection->fetchAll($select); + $this->aclDataCache->save($this->serializer->serialize($rolesArray), $this->cacheKey); + return $rolesArray; + } } diff --git a/app/code/Magento/Authorization/Model/Acl/Loader/Rule.php b/app/code/Magento/Authorization/Model/Acl/Loader/Rule.php index 2fa86b63ae504..8f89e00cfb22e 100644 --- a/app/code/Magento/Authorization/Model/Acl/Loader/Rule.php +++ b/app/code/Magento/Authorization/Model/Acl/Loader/Rule.php @@ -1,32 +1,69 @@ _resource = $resource; $this->_rootResource = $rootResource; + $this->aclDataCache = $aclDataCache ?: ObjectManager::getInstance()->get( + \Magento\Framework\Acl\Data\CacheInterface::class + ); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->cacheKey = $cacheKey; } /** @@ -37,15 +74,7 @@ public function __construct( */ public function populateAcl(\Magento\Framework\Acl $acl) { - $ruleTable = $this->_resource->getTableName("authorization_rule"); - - $connection = $this->_resource->getConnection(); - - $select = $connection->select()->from(['r' => $ruleTable]); - - $rulesArr = $connection->fetchAll($select); - - foreach ($rulesArr as $rule) { + foreach ($this->getRulesArray() as $rule) { $role = $rule['role_id']; $resource = $rule['resource_id']; $privileges = !empty($rule['privileges']) ? explode(',', $rule['privileges']) : null; @@ -62,4 +91,28 @@ public function populateAcl(\Magento\Framework\Acl $acl) } } } + + /** + * Get application ACL rules array. + * + * @return array + */ + private function getRulesArray() + { + $rulesCachedData = $this->aclDataCache->load($this->cacheKey); + if ($rulesCachedData) { + return $this->serializer->unserialize($rulesCachedData); + } + + $ruleTable = $this->_resource->getTableName("authorization_rule"); + $connection = $this->_resource->getConnection(); + $select = $connection->select() + ->from(['r' => $ruleTable]); + + $rulesArr = $connection->fetchAll($select); + + $this->aclDataCache->save($this->serializer->serialize($rulesArr), $this->cacheKey); + + return $rulesArr; + } } diff --git a/app/code/Magento/Authorization/Model/Acl/Role/Generic.php b/app/code/Magento/Authorization/Model/Acl/Role/Generic.php index f35a755b74045..b6a92d5ccae27 100644 --- a/app/code/Magento/Authorization/Model/Acl/Role/Generic.php +++ b/app/code/Magento/Authorization/Model/Acl/Role/Generic.php @@ -1,6 +1,6 @@ _aclBuilder = $aclBuilder; parent::__construct($context, $connectionName); $this->_rootResource = $rootResource; $this->_aclCache = $aclCache; $this->_logger = $logger; + $this->aclDataCache = $aclDataCache ?: ObjectManager::getInstance()->get( + \Magento\Framework\Acl\Data\CacheInterface::class + ); } /** @@ -79,8 +92,8 @@ protected function _construct() */ public function saveRel(\Magento\Authorization\Model\Rules $rule) { + $connection = $this->getConnection(); try { - $connection = $this->getConnection(); $connection->beginTransaction(); $roleId = $rule->getRoleId(); @@ -118,7 +131,7 @@ public function saveRel(\Magento\Authorization\Model\Rules $rule) } $connection->commit(); - $this->_aclCache->clean(); + $this->aclDataCache->clean(); } catch (\Magento\Framework\Exception\LocalizedException $e) { $connection->rollBack(); throw $e; diff --git a/app/code/Magento/Authorization/Model/ResourceModel/Rules/Collection.php b/app/code/Magento/Authorization/Model/ResourceModel/Rules/Collection.php index e4bb8f1195322..9aec756931868 100644 --- a/app/code/Magento/Authorization/Model/ResourceModel/Rules/Collection.php +++ b/app/code/Magento/Authorization/Model/ResourceModel/Rules/Collection.php @@ -1,6 +1,6 @@ _resourceMock = $this->getMock( @@ -57,51 +72,82 @@ protected function setUp() false ); - $this->_resourceMock->expects( - $this->once() - )->method( - 'getTableName' - )->with( - $this->equalTo('authorization_role') - )->will( - $this->returnArgument(1) - ); - - $selectMock = $this->getMock(\Magento\Framework\DB\Select::class, [], [], '', false); - $selectMock->expects($this->any())->method('from')->will($this->returnValue($selectMock)); + $this->selectMock = $this->getMock(\Magento\Framework\DB\Select::class, [], [], '', false); + $this->selectMock->expects($this->any()) + ->method('from') + ->will($this->returnValue($this->selectMock)); $this->_adapterMock = $this->getMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class, [], [], '', false); - $this->_adapterMock->expects($this->once())->method('select')->will($this->returnValue($selectMock)); - - $this->_resourceMock->expects( - $this->once() - )->method( - 'getConnection' - )->will( - $this->returnValue($this->_adapterMock) + + $this->serializerMock = $this->getMock( + \Magento\Framework\Serialize\Serializer\Json::class, + ['serialize', 'unserialize'], + [], + '', + false + ); + $this->serializerMock->expects($this->any()) + ->method('serialize') + ->will( + $this->returnCallback( + function ($value) { + return json_encode($value); + } + ) + ); + + $this->serializerMock->expects($this->any()) + ->method('unserialize') + ->will( + $this->returnCallback( + function ($value) { + return json_decode($value, true); + } + ) + ); + + $this->aclDataCacheMock = $this->getMock( + \Magento\Framework\Acl\Data\CacheInterface::class, + [], + [], + '', + false ); $this->_model = new \Magento\Authorization\Model\Acl\Loader\Role( $this->_groupFactoryMock, $this->_roleFactoryMock, - $this->_resourceMock + $this->_resourceMock, + $this->aclDataCacheMock, + $this->serializerMock ); } public function testPopulateAclAddsRolesAndTheirChildren() { - $this->_adapterMock->expects( - $this->once() - )->method( - 'fetchAll' - )->will( - $this->returnValue( - [ - ['role_id' => 1, 'role_type' => 'G', 'parent_id' => null], - ['role_id' => 2, 'role_type' => 'U', 'parent_id' => 1, 'user_id' => 1], - ] - ) - ); + $this->_resourceMock->expects($this->once()) + ->method('getTableName') + ->with($this->equalTo('authorization_role')) + ->will($this->returnArgument(1)); + + $this->_adapterMock->expects($this->once()) + ->method('select') + ->will($this->returnValue($this->selectMock)); + + $this->_resourceMock->expects($this->once()) + ->method('getConnection') + ->will($this->returnValue($this->_adapterMock)); + + $this->_adapterMock->expects($this->once()) + ->method('fetchAll') + ->will( + $this->returnValue( + [ + ['role_id' => 1, 'role_type' => 'G', 'parent_id' => null], + ['role_id' => 2, 'role_type' => 'U', 'parent_id' => 1, 'user_id' => 1], + ] + ) + ); $this->_groupFactoryMock->expects($this->once())->method('create')->with(['roleId' => '1']); $this->_roleFactoryMock->expects($this->once())->method('create')->with(['roleId' => '2']); @@ -115,13 +161,55 @@ public function testPopulateAclAddsRolesAndTheirChildren() public function testPopulateAclAddsMultipleParents() { - $this->_adapterMock->expects( - $this->once() - )->method( - 'fetchAll' - )->will( - $this->returnValue([['role_id' => 1, 'role_type' => 'U', 'parent_id' => 2, 'user_id' => 3]]) - ); + $this->_resourceMock->expects($this->once()) + ->method('getTableName') + ->with($this->equalTo('authorization_role')) + ->will($this->returnArgument(1)); + + $this->_adapterMock->expects($this->once()) + ->method('select') + ->will($this->returnValue($this->selectMock)); + + $this->_resourceMock->expects($this->once()) + ->method('getConnection') + ->will($this->returnValue($this->_adapterMock)); + + $this->_adapterMock->expects($this->once()) + ->method('fetchAll') + ->will($this->returnValue([['role_id' => 1, 'role_type' => 'U', 'parent_id' => 2, 'user_id' => 3]])); + + $this->_roleFactoryMock->expects($this->never())->method('getModelInstance'); + $this->_groupFactoryMock->expects($this->never())->method('getModelInstance'); + + $aclMock = $this->getMock(\Magento\Framework\Acl::class); + $aclMock->expects($this->at(0))->method('hasRole')->with('1')->will($this->returnValue(true)); + $aclMock->expects($this->at(1))->method('addRoleParent')->with('1', '2'); + + $this->_model->populateAcl($aclMock); + } + + public function testPopulateAclFromCache() + { + $this->_resourceMock->expects($this->never())->method('getConnection'); + $this->_resourceMock->expects($this->never())->method('getTableName'); + $this->_adapterMock->expects($this->never())->method('fetchAll'); + $this->aclDataCacheMock->expects($this->once()) + ->method('load') + ->with(\Magento\Authorization\Model\Acl\Loader\Role::ACL_ROLES_CACHE_KEY) + ->will( + $this->returnValue( + json_encode( + [ + [ + 'role_id' => 1, + 'role_type' => 'U', + 'parent_id' => 2, + 'user_id' => 3 + ] + ] + ) + ) + ); $this->_roleFactoryMock->expects($this->never())->method('getModelInstance'); $this->_groupFactoryMock->expects($this->never())->method('getModelInstance'); diff --git a/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RuleTest.php b/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RuleTest.php index f5f3b9b6a4b9f..2cf4b6d997fee 100644 --- a/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RuleTest.php +++ b/app/code/Magento/Authorization/Test/Unit/Model/Acl/Loader/RuleTest.php @@ -1,6 +1,6 @@ _resourceMock = $this->getMock( @@ -32,39 +42,71 @@ protected function setUp() false, false ); + $this->serializerMock = $this->getMock( + \Magento\Framework\Serialize\Serializer\Json::class, + ['serialize', 'unserialize'], + [], + '', + false + ); + $this->serializerMock->expects($this->any()) + ->method('serialize') + ->will( + $this->returnCallback( + function ($value) { + return json_encode($value); + } + ) + ); + + $this->serializerMock->expects($this->any()) + ->method('unserialize') + ->will( + $this->returnCallback( + function ($value) { + return json_decode($value, true); + } + ) + ); + + $this->aclDataCacheMock = $this->getMock( + \Magento\Framework\Acl\Data\CacheInterface::class, + [], + [], + '', + false + ); + $this->_rootResourceMock = new \Magento\Framework\Acl\RootResource('Magento_Backend::all'); $this->_model = new \Magento\Authorization\Model\Acl\Loader\Rule( $this->_rootResourceMock, - $this->_resourceMock + $this->_resourceMock, + [], + $this->aclDataCacheMock, + $this->serializerMock ); } - public function testPopulateAcl() + public function testPopulateAclFromCache() { - $this->_resourceMock->expects($this->any())->method('getTable')->will($this->returnArgument(1)); - - $selectMock = $this->getMock(\Magento\Framework\DB\Select::class, [], [], '', false); - $selectMock->expects($this->any())->method('from')->will($this->returnValue($selectMock)); - - $connectionMock = $this->getMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class, [], [], '', false); - $connectionMock->expects($this->once())->method('select')->will($this->returnValue($selectMock)); - $connectionMock->expects( - $this->once() - )->method( - 'fetchAll' - )->will( - $this->returnValue( - [ - ['role_id' => 1, 'resource_id' => 'Magento_Backend::all', 'permission' => 'allow'], - ['role_id' => 2, 'resource_id' => 1, 'permission' => 'allow'], - ['role_id' => 3, 'resource_id' => 1, 'permission' => 'deny'], - ] - ) - ); + $this->_resourceMock->expects($this->never())->method('getTable'); + $this->_resourceMock->expects($this->never()) + ->method('getConnection'); - $this->_resourceMock->expects($this->once()) - ->method('getConnection') - ->will($this->returnValue($connectionMock)); + $this->aclDataCacheMock->expects($this->once()) + ->method('load') + ->with(\Magento\Authorization\Model\Acl\Loader\Rule::ACL_RULE_CACHE_KEY) + ->will( + $this->returnValue( + json_encode( + [ + ['role_id' => 1, 'resource_id' => 'Magento_Backend::all', 'permission' => 'allow'], + ['role_id' => 2, 'resource_id' => 1, 'permission' => 'allow'], + ['role_id' => 3, 'resource_id' => 1, 'permission' => 'deny'], + ] + ) + ) + ); $aclMock = $this->getMock(\Magento\Framework\Acl::class); $aclMock->expects($this->any())->method('has')->will($this->returnValue(true)); diff --git a/app/code/Magento/Authorization/Test/Unit/Model/CompositeUserContextTest.php b/app/code/Magento/Authorization/Test/Unit/Model/CompositeUserContextTest.php index 3724a97a08cd2..d899194bc9e95 100644 --- a/app/code/Magento/Authorization/Test/Unit/Model/CompositeUserContextTest.php +++ b/app/code/Magento/Authorization/Test/Unit/Model/CompositeUserContextTest.php @@ -1,6 +1,6 @@ contextMock = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\Context::class) + ->disableOriginalConstructor() + ->setMethods(['getResources']) + ->getMock(); + + $this->resourceConnectionMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) + ->disableOriginalConstructor() + ->setMethods(['getConnection', 'getTableName']) + ->getMock(); + + $this->contextMock->expects($this->once()) + ->method('getResources') + ->will($this->returnValue($this->resourceConnectionMock)); + + $this->connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->resourceConnectionMock->expects($this->once()) + ->method('getConnection') + ->with('connection') + ->will($this->returnValue($this->connectionMock)); + + $this->resourceConnectionMock->expects($this->any()) + ->method('getTableName') + ->with('authorization_rule', 'connection') + ->will($this->returnArgument(0)); + + $this->aclBuilderMock = $this->getMockBuilder(\Magento\Framework\Acl\Builder::class) + ->disableOriginalConstructor() + ->setMethods(['getConfigCache']) + ->getMock(); + + $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->rootResourceMock = $this->getMockBuilder(\Magento\Framework\Acl\RootResource::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->aclCacheMock = $this->getMockBuilder(\Magento\Framework\Acl\CacheInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->aclDataCacheMock = $this->getMockBuilder(\Magento\Framework\Acl\Data\CacheInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->aclBuilderMock->expects($this->any()) + ->method('getConfigCache') + ->will($this->returnValue($this->aclDataCacheMock)); + + $this->ruleMock = $this->getMockBuilder(\Magento\Authorization\Model\Rules::class) + ->disableOriginalConstructor() + ->setMethods(['getRoleId']) + ->getMock(); + + $this->ruleMock->expects($this->any()) + ->method('getRoleId') + ->will($this->returnValue(self::TEST_ROLE_ID)); + + $this->model = new \Magento\Authorization\Model\ResourceModel\Rules( + $this->contextMock, + $this->aclBuilderMock, + $this->loggerMock, + $this->rootResourceMock, + $this->aclCacheMock, + 'connection', + $this->aclDataCacheMock + ); + } + + /** + * Test save with no resources posted. + */ + public function testSaveRelNoResources() + { + $this->connectionMock->expects($this->once()) + ->method('beginTransaction'); + $this->connectionMock->expects($this->once()) + ->method('delete') + ->with('authorization_rule', ['role_id = ?' => self::TEST_ROLE_ID]); + $this->connectionMock->expects($this->once()) + ->method('commit'); + + $this->aclDataCacheMock->expects($this->once()) + ->method('clean'); + + $this->model->saveRel($this->ruleMock); + } + + /** + * Test LocalizedException throw case. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage TestException + */ + public function testLocalizedExceptionOccurance() + { + $exceptionPhrase = $this->getMockBuilder(\Magento\Framework\Phrase::class) + ->disableOriginalConstructor() + ->setMethods(['render']) + ->getMock(); + + $exceptionPhrase->expects($this->any())->method('render')->will($this->returnValue('TestException')); + + $exception = new \Magento\Framework\Exception\LocalizedException($exceptionPhrase); + + $this->connectionMock->expects($this->once()) + ->method('beginTransaction'); + + $this->connectionMock->expects($this->once()) + ->method('delete') + ->with('authorization_rule', ['role_id = ?' => self::TEST_ROLE_ID]) + ->will($this->throwException($exception)); + + $this->connectionMock->expects($this->once())->method('rollBack'); + + $this->model->saveRel($this->ruleMock); + } + + /** + * Test generic exception throw case. + */ + public function testGenericExceptionOccurance() + { + $exception = new \Exception('GenericException'); + + $this->connectionMock->expects($this->once()) + ->method('beginTransaction'); + + $this->connectionMock->expects($this->once()) + ->method('delete') + ->with('authorization_rule', ['role_id = ?' => self::TEST_ROLE_ID]) + ->will($this->throwException($exception)); + + $this->connectionMock->expects($this->once())->method('rollBack'); + $this->loggerMock->expects($this->once())->method('critical')->with($exception); + + $this->model->saveRel($this->ruleMock); + } +} diff --git a/app/code/Magento/Authorization/composer.json b/app/code/Magento/Authorization/composer.json index 0ca367d4854df..af88e8376dc75 100644 --- a/app/code/Magento/Authorization/composer.json +++ b/app/code/Magento/Authorization/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-authorization", "description": "Authorization module provides access to Magento ACL functionality.", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Authorization/etc/di.xml b/app/code/Magento/Authorization/etc/di.xml index 1159fb0a7606e..9370e14d81a35 100644 --- a/app/code/Magento/Authorization/etc/di.xml +++ b/app/code/Magento/Authorization/etc/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorization/etc/module.xml b/app/code/Magento/Authorization/etc/module.xml index f3fa8793f3e57..f27253870499d 100644 --- a/app/code/Magento/Authorization/etc/module.xml +++ b/app/code/Magento/Authorization/etc/module.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorization/registration.php b/app/code/Magento/Authorization/registration.php index 6aabf5a16bb91..f35b17a4d6710 100644 --- a/app/code/Magento/Authorization/registration.php +++ b/app/code/Magento/Authorization/registration.php @@ -1,6 +1,6 @@ dataFactory->create($area); $params = []; - $data = $this->getRequest()->getPostValue(); + $data = $this->getRequest()->getParams(); + /* @var $paymentMethod \Magento\Authorizenet\Model\DirectPost */ $paymentMethod = $this->_objectManager->create(\Magento\Authorizenet\Model\Directpost::class); @@ -110,9 +111,8 @@ protected function _responseAction($area = 'frontend') $params['redirect'] = $helper->getRedirectIframeUrl($result); } + //registering parameter for iframe content $this->_coreRegistry->register(Iframe::REGISTRY_KEY, $params); - $this->_view->addPageLayoutHandles(); - $this->_view->loadLayout(false)->renderLayout(); } /** diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php index c0ac07a11adb3..6e2401d930b16 100644 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php +++ b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/BackendResponse.php @@ -1,7 +1,7 @@ _responseAction('adminhtml'); + return $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_PAGE); } } diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php index 2816a3bae78b9..bc327caffb262 100644 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php +++ b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php @@ -1,6 +1,6 @@ eventManager = $context->getEventManager(); $this->cartManagement = $cartManagement; $this->onepageCheckout = $onepageCheckout; $this->jsonHelper = $jsonHelper; + $this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class); parent::__construct($context, $coreRegistry, $dataFactory); } @@ -127,9 +139,11 @@ protected function placeCheckoutOrder() ] ); } catch (LocalizedException $exception) { + $this->logger->critical($exception); $result->setData('error', true); $result->setData('error_messages', $exception->getMessage()); } catch (\Exception $exception) { + $this->logger->critical($exception); $result->setData('error', true); $result->setData( 'error_messages', diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php index 8745737a19bd6..84d91dc89f155 100644 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php +++ b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Redirect.php @@ -1,7 +1,7 @@ _responseAction('frontend'); + return $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_PAGE); } } diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php index 5bfe916160967..c542c16e9fa7e 100644 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php +++ b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/ReturnQuote.php @@ -1,7 +1,7 @@ setXCity($billing->getCity()) ->setXState($billing->getRegion()) ->setXZip($billing->getPostcode()) - ->setXCountry($billing->getCountry()) + ->setXCountry($billing->getCountryId()) ->setXPhone($billing->getTelephone()) ->setXFax($billing->getFax()) ->setXCustId($order->getCustomerId()) @@ -352,7 +352,7 @@ protected function buildRequest(\Magento\Framework\DataObject $payment) ->setXShipToCity($shipping->getCity()) ->setXShipToState($shipping->getRegion()) ->setXShipToZip($shipping->getPostcode()) - ->setXShipToCountry($shipping->getCountry()); + ->setXShipToCountry($shipping->getCountryId()); } $request->setXPoNum($payment->getPoNumber()) diff --git a/app/code/Magento/Authorizenet/Model/Debug.php b/app/code/Magento/Authorizenet/Model/Debug.php index 3e46731197660..7605aa762a183 100644 --- a/app/code/Magento/Authorizenet/Model/Debug.php +++ b/app/code/Magento/Authorizenet/Model/Debug.php @@ -1,6 +1,6 @@ setXCity(strval($billing->getCity())) ->setXState(strval($billing->getRegion())) ->setXZip(strval($billing->getPostcode())) - ->setXCountry(strval($billing->getCountry())) + ->setXCountry(strval($billing->getCountryId())) ->setXPhone(strval($billing->getTelephone())) ->setXFax(strval($billing->getFax())) ->setXCustId(strval($billing->getCustomerId())) @@ -151,7 +151,7 @@ public function setDataFromOrder( )->setXShipToZip( strval($shipping->getPostcode()) )->setXShipToCountry( - strval($shipping->getCountry()) + strval($shipping->getCountryId()) ); } diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php b/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php index 7ef6647285616..d0f6ea6515d11 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Request/Factory.php @@ -1,6 +1,6 @@ getMockBuilder(\Magento\Framework\App\Response\Http::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->loggerMock = $this + ->getMockBuilder(\Psr\Log\LoggerInterface::class) + ->getMock(); $this->objectManager = new ObjectManager($this); $this->placeOrderController = $this->objectManager->getObject( @@ -165,6 +174,7 @@ protected function setUp() 'cartManagement' => $this->cartManagementMock, 'onepageCheckout' => $this->onepageCheckout, 'jsonHelper' => $this->jsonHelperMock, + 'logger' => $this->loggerMock, ] ); } @@ -214,13 +224,15 @@ public function testExecute( * @param $controller * @param $quoteId * @param $result + * @param \Exception $exception Exception to check * @dataProvider textExecuteFailedPlaceOrderDataProvider */ public function testExecuteFailedPlaceOrder( $paymentMethod, $controller, $quoteId, - $result + $result, + $exception ) { $this->requestMock->expects($this->at(0)) ->method('getParam') @@ -238,7 +250,11 @@ public function testExecuteFailedPlaceOrder( $this->cartManagementMock->expects($this->once()) ->method('placeOrder') - ->willThrowException(new \Exception()); + ->willThrowException($exception); + + $this->loggerMock->expects($this->once()) + ->method('critical') + ->with($exception); $this->jsonHelperMock->expects($this->any()) ->method('jsonEncode') @@ -278,11 +294,19 @@ public function textExecuteDataProvider() */ public function textExecuteFailedPlaceOrderDataProvider() { - $objectFailed = new \Magento\Framework\DataObject(); - $objectFailed->setData('error', true); - $objectFailed->setData( - 'error_messages', - __('An error occurred on the server. Please try to place the order again.') + $objectFailed1 = new \Magento\Framework\DataObject( + [ + 'error' => true, + 'error_messages' => __('An error occurred on the server. Please try to place the order again.') + ] + ); + $generalException = new \Exception('Exception logging will save the world!'); + $localizedException = new LocalizedException(__('Electronic payments save the trees.')); + $objectFailed2 = new \Magento\Framework\DataObject( + [ + 'error' => true, + 'error_messages' => $localizedException->getMessage() + ] ); return [ @@ -290,7 +314,15 @@ public function textExecuteFailedPlaceOrderDataProvider() ['method' => 'authorizenet_directpost'], IframeConfigProvider::CHECKOUT_IDENTIFIER, 1, - $objectFailed + $objectFailed1, + $generalException, + ], + [ + ['method' => 'authorizenet_directpost'], + IframeConfigProvider::CHECKOUT_IDENTIFIER, + 1, + $objectFailed2, + $localizedException, ], ]; } diff --git a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php index d11a7b8df3521..4ec5e401c9e96 100644 --- a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php +++ b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/RedirectTest.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/events.xml b/app/code/Magento/Authorizenet/etc/adminhtml/events.xml index 4a669cd7d9cac..8114b8c8312cc 100644 --- a/app/code/Magento/Authorizenet/etc/adminhtml/events.xml +++ b/app/code/Magento/Authorizenet/etc/adminhtml/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml b/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml index ef3e63f2c9c90..028e1a8500d07 100644 --- a/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml +++ b/app/code/Magento/Authorizenet/etc/adminhtml/routes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml index fd2fb84f0a290..3d4cde185dcd1 100644 --- a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml +++ b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/etc/config.xml b/app/code/Magento/Authorizenet/etc/config.xml index 70b413b5c1a39..f5b053003f1f2 100644 --- a/app/code/Magento/Authorizenet/etc/config.xml +++ b/app/code/Magento/Authorizenet/etc/config.xml @@ -1,7 +1,7 @@ @@ -10,7 +10,7 @@ 0 - AE,VI,MC,DI + AE,VI,MC,DI,JCB,DN 0 0 diff --git a/app/code/Magento/Authorizenet/etc/di.xml b/app/code/Magento/Authorizenet/etc/di.xml index f5e595fb450e8..3bd70f25a3bf9 100644 --- a/app/code/Magento/Authorizenet/etc/di.xml +++ b/app/code/Magento/Authorizenet/etc/di.xml @@ -1,7 +1,7 @@ @@ -16,4 +16,14 @@ Magento\Authorizenet\Model\Directpost\Session\Storage + + + + 1 + 1 + 1 + 1 + + + diff --git a/app/code/Magento/Authorizenet/etc/frontend/di.xml b/app/code/Magento/Authorizenet/etc/frontend/di.xml index 7f20e067cc426..8dcf8ed700dcb 100644 --- a/app/code/Magento/Authorizenet/etc/frontend/di.xml +++ b/app/code/Magento/Authorizenet/etc/frontend/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/etc/frontend/events.xml b/app/code/Magento/Authorizenet/etc/frontend/events.xml index bb11f4bca363a..2c6e3f12a9196 100644 --- a/app/code/Magento/Authorizenet/etc/frontend/events.xml +++ b/app/code/Magento/Authorizenet/etc/frontend/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/etc/frontend/page_types.xml b/app/code/Magento/Authorizenet/etc/frontend/page_types.xml index 6dc50d5d28dd9..be4692b135955 100644 --- a/app/code/Magento/Authorizenet/etc/frontend/page_types.xml +++ b/app/code/Magento/Authorizenet/etc/frontend/page_types.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/etc/frontend/routes.xml b/app/code/Magento/Authorizenet/etc/frontend/routes.xml index 7da347a2e6381..1bdcff9f1efe1 100644 --- a/app/code/Magento/Authorizenet/etc/frontend/routes.xml +++ b/app/code/Magento/Authorizenet/etc/frontend/routes.xml @@ -1,7 +1,7 @@ @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Authorizenet/etc/frontend/sections.xml b/app/code/Magento/Authorizenet/etc/frontend/sections.xml index 33c48e60e8dab..977a4b14e3e14 100644 --- a/app/code/Magento/Authorizenet/etc/frontend/sections.xml +++ b/app/code/Magento/Authorizenet/etc/frontend/sections.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/etc/module.xml b/app/code/Magento/Authorizenet/etc/module.xml index 75317e254efaa..91d93e56e0cad 100644 --- a/app/code/Magento/Authorizenet/etc/module.xml +++ b/app/code/Magento/Authorizenet/etc/module.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/i18n/en_US.csv b/app/code/Magento/Authorizenet/i18n/en_US.csv index 7183c706dc0a2..45025304c4e44 100644 --- a/app/code/Magento/Authorizenet/i18n/en_US.csv +++ b/app/code/Magento/Authorizenet/i18n/en_US.csv @@ -64,3 +64,4 @@ Debug,Debug "Minimum Order Total","Minimum Order Total" "Maximum Order Total","Maximum Order Total" "Sort Order","Sort Order" +"Sorry, but something went wrong. Please contact the seller.","Sorry, but something went wrong. Please contact the seller." diff --git a/app/code/Magento/Authorizenet/registration.php b/app/code/Magento/Authorizenet/registration.php index a0eab4323313e..ad98beafa744d 100644 --- a/app/code/Magento/Authorizenet/registration.php +++ b/app/code/Magento/Authorizenet/registration.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml index aeed400c3c8fa..851cbf398750d 100644 --- a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml +++ b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_index.xml @@ -1,7 +1,7 @@ @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml index 21e8f1d70fbb5..ec5ce845b8521 100644 --- a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml +++ b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml @@ -1,7 +1,7 @@ @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml index d81573c891c9f..7470afcfdb7d0 100644 --- a/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml +++ b/app/code/Magento/Authorizenet/view/adminhtml/layout/sales_order_view.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml b/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml index ae5a5ec6217ed..2b95af46a6b47 100644 --- a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml +++ b/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/iframe.phtml @@ -1,6 +1,6 @@ getInfoData('cc_exp_year');
    +
    -
    -
    + +
    @@ -66,7 +76,11 @@ $ccExpYear = $block->getInfoData('cc_exp_year');
    + hasVerification()): ?> -
    -
    - \ No newline at end of file + diff --git a/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js b/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js index 69dd63cf47caa..b9bb218d74190 100644 --- a/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js +++ b/app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js @@ -1,7 +1,8 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + (function (factory) { if (typeof define === 'function' && define.amd) { define([ diff --git a/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_backendresponse.xml b/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_backendresponse.xml index 97f425e78c7de..1eedd5e26a3ed 100644 --- a/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_backendresponse.xml +++ b/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_backendresponse.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_redirect.xml b/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_redirect.xml index 97f425e78c7de..1eedd5e26a3ed 100644 --- a/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_redirect.xml +++ b/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_redirect.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_response.xml b/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_response.xml index 97f425e78c7de..1eedd5e26a3ed 100644 --- a/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_response.xml +++ b/app/code/Magento/Authorizenet/view/frontend/layout/authorizenet_directpost_payment_response.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Authorizenet/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/Authorizenet/view/frontend/layout/checkout_index_index.xml index 489dddc9b15d6..1025207fec1d5 100644 --- a/app/code/Magento/Authorizenet/view/frontend/layout/checkout_index_index.xml +++ b/app/code/Magento/Authorizenet/view/frontend/layout/checkout_index_index.xml @@ -1,7 +1,7 @@ @@ -46,4 +46,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js b/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js index 4af97d5775a7f..26c0e732303d0 100644 --- a/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js +++ b/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/authorizenet.js b/app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/authorizenet.js index 83b3984735563..1ae78194b97a1 100644 --- a/app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/authorizenet.js +++ b/app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/authorizenet.js @@ -1,26 +1,22 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -/*browser:true*/ -/*global define*/ -define( - [ - 'uiComponent', - 'Magento_Checkout/js/model/payment/renderer-list' - ], - function ( - Component, - rendererList - ) { - 'use strict'; - rendererList.push( - { - type: 'authorizenet_directpost', - component: 'Magento_Authorizenet/js/view/payment/method-renderer/authorizenet-directpost' - } - ); - /** Add view logic here if needed */ - return Component.extend({}); - } -); \ No newline at end of file + +define([ + 'uiComponent', + 'Magento_Checkout/js/model/payment/renderer-list' +], +function (Component, rendererList) { + 'use strict'; + + rendererList.push( + { + type: 'authorizenet_directpost', + component: 'Magento_Authorizenet/js/view/payment/method-renderer/authorizenet-directpost' + } + ); + + /** Add view logic here if needed */ + return Component.extend({}); +}); diff --git a/app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/method-renderer/authorizenet-directpost.js b/app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/method-renderer/authorizenet-directpost.js index cd05960c17633..989e24cada386 100644 --- a/app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/method-renderer/authorizenet-directpost.js +++ b/app/code/Magento/Authorizenet/view/frontend/web/js/view/payment/method-renderer/authorizenet-directpost.js @@ -1,64 +1,64 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -define( - [ - 'jquery', - 'Magento_Payment/js/view/payment/iframe' - ], - function ($, Component) { - 'use strict'; - return Component.extend({ - defaults: { - template: 'Magento_Authorizenet/payment/authorizenet-directpost', - timeoutMessage: 'Sorry, but something went wrong. Please contact the seller.' - }, - placeOrderHandler: null, - validateHandler: null, +define([ + 'jquery', + 'Magento_Payment/js/view/payment/iframe', + 'mage/translate' +], +function ($, Component, $t) { + 'use strict'; - /** - * @param {Object} handler - */ - setPlaceOrderHandler: function (handler) { - this.placeOrderHandler = handler; - }, + return Component.extend({ + defaults: { + template: 'Magento_Authorizenet/payment/authorizenet-directpost', + timeoutMessage: $t('Sorry, but something went wrong. Please contact the seller.') + }, + placeOrderHandler: null, + validateHandler: null, - /** - * @param {Object} handler - */ - setValidateHandler: function (handler) { - this.validateHandler = handler; - }, + /** + * @param {Object} handler + */ + setPlaceOrderHandler: function (handler) { + this.placeOrderHandler = handler; + }, - /** - * @returns {Object} - */ - context: function () { - return this; - }, + /** + * @param {Object} handler + */ + setValidateHandler: function (handler) { + this.validateHandler = handler; + }, - /** - * @returns {Boolean} - */ - isShowLegend: function () { - return true; - }, + /** + * @returns {Object} + */ + context: function () { + return this; + }, - /** - * @returns {String} - */ - getCode: function () { - return 'authorizenet_directpost'; - }, + /** + * @returns {Boolean} + */ + isShowLegend: function () { + return true; + }, - /** - * @returns {Boolean} - */ - isActive: function () { - return true; - } - }); - } -); + /** + * @returns {String} + */ + getCode: function () { + return 'authorizenet_directpost'; + }, + + /** + * @returns {Boolean} + */ + isActive: function () { + return true; + } + }); +}); diff --git a/app/code/Magento/Authorizenet/view/frontend/web/template/payment/authorizenet-directpost.html b/app/code/Magento/Authorizenet/view/frontend/web/template/payment/authorizenet-directpost.html index 1aff94817c6eb..2de6cad54d267 100644 --- a/app/code/Magento/Authorizenet/view/frontend/web/template/payment/authorizenet-directpost.html +++ b/app/code/Magento/Authorizenet/view/frontend/web/template/payment/authorizenet-directpost.html @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/App/AbstractAction.php b/app/code/Magento/Backend/App/AbstractAction.php index f0973b8f66a65..4efd013f43a1c 100644 --- a/app/code/Magento/Backend/App/AbstractAction.php +++ b/app/code/Magento/Backend/App/AbstractAction.php @@ -1,6 +1,6 @@ _scopePool = $scopePool; + $this->appConfig = $appConfig; } /** - * Retrieve config value by path and scope - * - * @param string $path - * @return mixed + * @inheritdoc */ public function getValue($path) { - return $this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)->getValue($path); + if (isset($this->data[$path])) { + return $this->data[$path]; + } + + $configPath = ScopeConfigInterface::SCOPE_TYPE_DEFAULT; + if ($path) { + $configPath .= '/' . $path; + } + return $this->appConfig->get(System::CONFIG_TYPE, $configPath); } /** - * Set config value in the corresponding config scope - * - * @param string $path - * @param mixed $value - * @return void + * @inheritdoc */ public function setValue($path, $value) { - $this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)->setValue($path, $value); + $this->data[$path] = $value; } /** - * Retrieve config flag - * - * @param string $path - * @return bool + * @inheritdoc */ public function isSetFlag($path) { - return !!$this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)->getValue($path); + $configPath = ScopeConfigInterface::SCOPE_TYPE_DEFAULT; + if ($path) { + $configPath .= '/' . $path; + } + return (bool) $this->appConfig->get(System::CONFIG_TYPE, $configPath); } } diff --git a/app/code/Magento/Backend/App/ConfigInterface.php b/app/code/Magento/Backend/App/ConfigInterface.php index 4000b54cc9834..1e183bc8fd1bd 100644 --- a/app/code/Magento/Backend/App/ConfigInterface.php +++ b/app/code/Magento/Backend/App/ConfigInterface.php @@ -2,7 +2,7 @@ /** * Default application path for backend area * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Backend\App; @@ -15,6 +15,8 @@ interface ConfigInterface /** * Retrieve config value by path * + * Path should looks like keys imploded by "/". For example scopes/stores/admin + * * @param string $path * @return mixed * @api @@ -24,6 +26,7 @@ public function getValue($path); /** * Set config value * + * @deprecated * @param string $path * @param mixed $value * @return void @@ -34,6 +37,8 @@ public function setValue($path, $value); /** * Retrieve config flag * + * Path should looks like keys imploded by "/". For example scopes/stores/admin + * * @param string $path * @return bool * @api diff --git a/app/code/Magento/Backend/App/DefaultPath.php b/app/code/Magento/Backend/App/DefaultPath.php index ef3d502db8ab8..9a25e328bde52 100644 --- a/app/code/Magento/Backend/App/DefaultPath.php +++ b/app/code/Magento/Backend/App/DefaultPath.php @@ -2,7 +2,7 @@ /** * Default application path for backend area * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Backend\App; diff --git a/app/code/Magento/Backend/App/Request/PathInfoProcessor.php b/app/code/Magento/Backend/App/Request/PathInfoProcessor.php index 08e115e9ec795..e0890ce917897 100644 --- a/app/code/Magento/Backend/App/Request/PathInfoProcessor.php +++ b/app/code/Magento/Backend/App/Request/PathInfoProcessor.php @@ -2,7 +2,7 @@ /** * Prevents path info processing for admin store * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Backend\App\Request; diff --git a/app/code/Magento/Backend/App/Response/Http/FileFactory.php b/app/code/Magento/Backend/App/Response/Http/FileFactory.php index dec52cdb646b0..1c67275a7fc67 100644 --- a/app/code/Magento/Backend/App/Response/Http/FileFactory.php +++ b/app/code/Magento/Backend/App/Response/Http/FileFactory.php @@ -1,6 +1,6 @@ menuItemChecker = $menuItemChecker; + $this->escaper = $escaper; + } + + /** + * Render menu item anchor. + * + * It is used in backend menu to render anchor menu. + * + * @param Item|false $activeItem Can be false if menu item is inaccessible + * but was triggered directly using controller. It is a legacy code behaviour. + * @param Item $menuItem + * @param int $level + * @return string + */ + public function renderAnchor($activeItem, Item $menuItem, $level) + { + if ($level == 1 && $menuItem->getUrl() == '#') { + $output = '' + . '' . $this->escaper->escapeHtml(__($menuItem->getTitle())) . '' + . ''; + } else { + $target = $menuItem->getTarget() ? ('target=' . $menuItem->getTarget()) : ''; + $output = '_renderItemAnchorTitle( + $menuItem + ) . $this->_renderItemOnclickFunction( + $menuItem + ) . ' class="' . ($this->menuItemChecker->isItemActive($activeItem, $menuItem, $level) ? '_active' : '') + . '">' . '' . $this->escaper->escapeHtml(__($menuItem->getTitle())) + . '' . ''; + } + + return $output; + } + + /** + * Render menu item anchor title + * + * @param Item $menuItem + * @return string + */ + private function _renderItemAnchorTitle($menuItem) + { + return $menuItem->hasTooltip() ? 'title="' . __($menuItem->getTooltip()) . '"' : ''; + } + + /** + * Render menu item onclick function + * + * @param Item $menuItem + * @return string + */ + private function _renderItemOnclickFunction($menuItem) + { + return $menuItem->hasClickCallback() ? ' onclick="' . $menuItem->getClickCallback() . '"' : ''; + } +} diff --git a/app/code/Magento/Backend/Block/Cache.php b/app/code/Magento/Backend/Block/Cache.php index 8b53882274182..b9a11fa5bb543 100644 --- a/app/code/Magento/Backend/Block/Cache.php +++ b/app/code/Magento/Backend/Block/Cache.php @@ -1,6 +1,6 @@ state = $state; + } + + /** + * {@inheritdoc} + */ + public function isVisible() + { + return $this->state->getMode() !== State::MODE_PRODUCTION; + } +} diff --git a/app/code/Magento/Backend/Block/Catalog/Product/Tab/Container.php b/app/code/Magento/Backend/Block/Catalog/Product/Tab/Container.php index bed5ee24434d9..49107a0ae4f7f 100644 --- a/app/code/Magento/Backend/Block/Catalog/Product/Tab/Container.php +++ b/app/code/Magento/Backend/Block/Catalog/Product/Tab/Container.php @@ -1,6 +1,6 @@ setCollection($collection); + parent::_prepareCollection(); + + /** @var Product $product */ + foreach ($collection as $product) { + $product->setPrice($product->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getValue()); + } - return parent::_prepareCollection(); + return $this; } /** diff --git a/app/code/Magento/Backend/Block/Dashboard/Totals.php b/app/code/Magento/Backend/Block/Dashboard/Totals.php index 342bece99299f..0e44a793c3422 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Totals.php +++ b/app/code/Magento/Backend/Block/Dashboard/Totals.php @@ -1,6 +1,6 @@ _url = $url; $this->_iteratorFactory = $iteratorFactory; $this->_authSession = $authSession; $this->_menuConfig = $menuConfig; $this->_localeResolver = $localeResolver; + $this->menuItemChecker = $menuItemChecker; + $this->anchorRenderer = $anchorRenderer; parent::__construct($context, $data); } @@ -99,30 +115,6 @@ protected function _construct() $this->setCacheTags([self::CACHE_TAGS]); } - /** - * Check whether given item is currently selected - * - * @param \Magento\Backend\Model\Menu\Item $item - * @param int $level - * @return bool - */ - protected function _isItemActive(\Magento\Backend\Model\Menu\Item $item, $level) - { - $itemModel = $this->getActiveItemModel(); - $output = false; - - if ($level == 0 && - $itemModel instanceof \Magento\Backend\Model\Menu\Item && - ($itemModel->getId() == $item->getId() || - $item->getChildren()->get( - $itemModel->getId() - ) !== null) - ) { - $output = true; - } - return $output; - } - /** * Render menu item anchor label * @@ -134,40 +126,6 @@ protected function _getAnchorLabel($menuItem) return $this->escapeHtml(__($menuItem->getTitle())); } - /** - * Render menu item anchor title - * - * @param \Magento\Backend\Model\Menu\Item $menuItem - * @return string - */ - protected function _renderItemAnchorTitle($menuItem) - { - return $menuItem->hasTooltip() ? 'title="' . __($menuItem->getTooltip()) . '"' : ''; - } - - /** - * Render menu item onclick function - * - * @param \Magento\Backend\Model\Menu\Item $menuItem - * @return string - */ - protected function _renderItemOnclickFunction($menuItem) - { - return $menuItem->hasClickCallback() ? ' onclick="' . $menuItem->getClickCallback() . '"' : ''; - } - - /** - * Render menu item anchor css class - * - * @param \Magento\Backend\Model\Menu\Item $menuItem - * @param int $level - * @return string - */ - protected function _renderAnchorCssClass($menuItem, $level) - { - return $this->_isItemActive($menuItem, $level) ? '_active' : ''; - } - /** * Render menu item mouse events * @param \Magento\Backend\Model\Menu\Item $menuItem @@ -188,10 +146,11 @@ protected function _renderMouseEvent($menuItem) protected function _renderItemCssClass($menuItem, $level) { $isLast = 0 == $level && (bool)$this->getMenuModel()->isLast($menuItem) ? 'last' : ''; - $output = ($this->_isItemActive( - $menuItem, - $level - ) ? '_current _active' : '') . + $output = ($this->menuItemChecker->isItemActive( + $this->getActiveItemModel(), + $menuItem, + $level + ) ? '_current _active' : '') . ' ' . ($menuItem->hasChildren() ? 'parent' : '') . ' ' . @@ -202,34 +161,6 @@ protected function _renderItemCssClass($menuItem, $level) return $output; } - /** - * Render menu item anchor - * @param \Magento\Backend\Model\Menu\Item $menuItem - * @param int $level - * @return string - */ - protected function _renderAnchor($menuItem, $level) - { - if ($level == 1 && $menuItem->getUrl() == '#') { - $output = '' - . '' . $this->_getAnchorLabel($menuItem) . '' - . ''; - } else { - $output = '_renderItemAnchorTitle( - $menuItem - ) . $this->_renderItemOnclickFunction( - $menuItem - ) . ' class="' . $this->_renderAnchorCssClass( - $menuItem, - $level - ) . '">' . '' . $this->_getAnchorLabel( - $menuItem - ) . '' . ''; - } - - return $output; - } - /** * Get menu filter iterator * @@ -336,7 +267,7 @@ public function renderMenu($menu, $level = 0) $menuItem->getId() ) . 'role="menuitem">'; - $output .= $this->_renderAnchor($menuItem, $level); + $output .= $this->anchorRenderer->renderAnchor($this->getActiveItemModel(), $menuItem, $level); if ($menuItem->hasChildren()) { $output .= $this->renderMenu($menuItem->getChildren(), $level + 1); @@ -456,7 +387,7 @@ public function renderNavigation($menu, $level = 0, $limit = 0, $colBrakes = []) $id = $this->getJsId($menuItem->getId()); $subMenu = $this->_addSubMenu($menuItem, $level, $limit, $id); - $anchor = $this->_renderAnchor($menuItem, $level); + $anchor = $this->anchorRenderer->renderAnchor($this->getActiveItemModel(), $menuItem, $level); $output .= '
  • getUiId($menuItem->getId()) . ' class="item-' . $itemClass . ' ' . $this->_renderItemCssClass($menuItem, $level) . ($level == 0 ? '" id="' . $id . '" aria-haspopup="true' : '') @@ -474,7 +405,7 @@ public function renderNavigation($menu, $level = 0, $limit = 0, $colBrakes = []) /** * Get current selected menu item * - * @return \Magento\Backend\Model\Menu\Item|null|bool + * @return \Magento\Backend\Model\Menu\Item|false */ public function getActiveItemModel() { diff --git a/app/code/Magento/Backend/Block/MenuItemChecker.php b/app/code/Magento/Backend/Block/MenuItemChecker.php new file mode 100644 index 0000000000000..b85b060cd379d --- /dev/null +++ b/app/code/Magento/Backend/Block/MenuItemChecker.php @@ -0,0 +1,49 @@ +isActiveItemEqualOrChild($activeItem, $item) + ) { + $output = true; + } + return $output; + } + + /** + * @param Item $activeItem, + * @param Item $item + * @return bool + */ + private function isActiveItemEqualOrChild($activeItem, $item) + { + return ($activeItem->getId() == $item->getId()) + || ($item->getChildren()->get($activeItem->getId()) !== null); + } +} diff --git a/app/code/Magento/Backend/Block/Page.php b/app/code/Magento/Backend/Block/Page.php index dca6a97a588c1..750eaebf58de6 100644 --- a/app/code/Magento/Backend/Block/Page.php +++ b/app/code/Magento/Backend/Block/Page.php @@ -1,6 +1,6 @@ getModuleName(); - } - - return !$this->_scopeConfig->isSetFlag( - 'advanced/modules_disable_output/' . $moduleName, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); + return true; } /** diff --git a/app/code/Magento/Backend/Block/Template/Context.php b/app/code/Magento/Backend/Block/Template/Context.php index 2cebd1cd5e49e..4f1324763f351 100644 --- a/app/code/Magento/Backend/Block/Template/Context.php +++ b/app/code/Magento/Backend/Block/Template/Context.php @@ -2,7 +2,7 @@ /** * Backend block template context * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/Backend/Block/Text/ListText.php b/app/code/Magento/Backend/Block/Text/ListText.php index 3ec68b25707b3..0dd8d0c05ac53 100644 --- a/app/code/Magento/Backend/Block/Text/ListText.php +++ b/app/code/Magento/Backend/Block/Text/ListText.php @@ -1,6 +1,6 @@ */ -namespace Magento\Backend\Block\Widget\Grid\Column\Renderer; - class Text extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** @@ -21,30 +20,53 @@ class Text extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRe protected $_variablePattern = '/\\$([a-z0-9_]+)/i'; /** - * Renders grid column + * Get value for the cel * - * @param \Magento\Framework\DataObject $row - * @return mixed + * @param DataObject $row + * @return string */ - public function _getValue(\Magento\Framework\DataObject $row) + public function _getValue(DataObject $row) { - $format = $this->getColumn()->getFormat() ? $this->getColumn()->getFormat() : null; - $defaultValue = $this->getColumn()->getDefault(); - if ($format === null) { - // If no format and it column not filtered specified return data as is. - $data = parent::_getValue($row); - $string = $data === null ? $defaultValue : $data; - return $this->escapeHtml($string); - } elseif (preg_match_all($this->_variablePattern, $format, $matches)) { - // Parsing of format string - $formattedString = $format; - foreach ($matches[0] as $matchIndex => $match) { - $value = $row->getData($matches[1][$matchIndex]); - $formattedString = str_replace($match, $value, $formattedString); + if (null === $this->getColumn()->getFormat()) { + return $this->getSimpleValue($row); + } + return $this->getFormattedValue($row); + } + + /** + * Get simple value + * + * @param DataObject $row + * @return string + */ + private function getSimpleValue(DataObject $row) + { + $data = parent::_getValue($row); + $value = null === $data ? $this->getColumn()->getDefault() : $data; + if (true === $this->getColumn()->getTranslate()) { + $value = __($value); + } + return $this->escapeHtml($value); + } + + /** + * Replace placeholders in the string with values + * + * @param DataObject $row + * @return string + */ + private function getFormattedValue(DataObject $row) + { + $value = $this->getColumn()->getFormat() ?: null; + if (true === $this->getColumn()->getTranslate()) { + $value = __($value); + } + if (preg_match_all($this->_variablePattern, $value, $matches)) { + foreach ($matches[0] as $index => $match) { + $replacement = $row->getData($matches[1][$index]); + $value = str_replace($match, $replacement, $value); } - return $formattedString; - } else { - return $this->escapeHtml($format); } + return $this->escapeHtml($value); } } diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Wrapline.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Wrapline.php index a45584438af5e..b11d14be78f98 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Wrapline.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Wrapline.php @@ -1,6 +1,6 @@ */ -namespace Magento\Backend\Block\Widget\Grid; - class Massaction extends \Magento\Backend\Block\Widget\Grid\Massaction\AbstractMassaction { } diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php index 2f3df1377b395..380b2991eb0d9 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php @@ -1,18 +1,18 @@ */ abstract class AbstractMassaction extends \Magento\Backend\Block\Widget { @@ -73,20 +73,21 @@ protected function _construct() * 'complete' => string, // Only for ajax enabled grid (optional) * 'url' => string, * 'confirm' => string, // text of confirmation of this action (optional) - * 'additional' => string // (optional) + * 'additional' => string, // (optional) + * 'visible' => object // instance of VisibilityCheckerInterface (optional) * ); * * @param string $itemId - * @param array|\Magento\Framework\DataObject $item + * @param array|DataObject $item * @return $this */ public function addItem($itemId, $item) { if (is_array($item)) { - $item = new \Magento\Framework\DataObject($item); + $item = new DataObject($item); } - if ($item instanceof \Magento\Framework\DataObject) { + if ($item instanceof DataObject && $this->isVisible($item)) { $item->setId($itemId); $item->setUrl($this->getUrl($item->getUrl())); $this->_items[$itemId] = $item; @@ -95,6 +96,19 @@ public function addItem($itemId, $item) return $this; } + /** + * Check that item can be added to list + * + * @param DataObject $item + * @return bool + */ + private function isVisible(DataObject $item) + { + /** @var VisibilityChecker $checker */ + $checker = $item->getData('visible'); + return (!$checker instanceof VisibilityChecker) || $checker->isVisible(); + } + /** * Retrieve massaction item with id $itemId * diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Additional.php b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Additional.php index 898e2efb508bd..15250b3904d2d 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Additional.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Additional.php @@ -1,6 +1,6 @@ getState()->getMode() === State::MODE_PRODUCTION) { + $this->messageManager->addErrorMessage(__('You can\'t change status of cache type(s) in production mode')); + } else { + $this->disableCache(); + } + + return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('adminhtml/*'); + } + + /** + * Disable cache + * + * @return void + */ + private function disableCache() { try { $types = $this->getRequest()->getParam('types'); @@ -41,9 +67,20 @@ public function execute() } catch (\Exception $e) { $this->messageManager->addException($e, __('An error occurred while disabling cache.')); } + } + + /** + * Get State Instance + * + * @return State + * @deprecated + */ + private function getState() + { + if ($this->state === null) { + $this->state = ObjectManager::getInstance()->get(State::class); + } - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); - return $resultRedirect->setPath('adminhtml/*'); + return $this->state; } } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php index 6c8bccfee166a..0cf09508c2932 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php @@ -1,22 +1,48 @@ getState()->getMode() === State::MODE_PRODUCTION) { + $this->messageManager->addErrorMessage(__('You can\'t change status of cache type(s) in production mode')); + } else { + $this->enableCache(); + } + + return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('adminhtml/*'); + } + + /** + * Enable cache + * + * @return void + */ + private function enableCache() { try { $types = $this->getRequest()->getParam('types'); @@ -40,9 +66,20 @@ public function execute() } catch (\Exception $e) { $this->messageManager->addException($e, __('An error occurred while enabling cache.')); } + } + + /** + * Get State Instance + * + * @return State + * @deprecated + */ + private function getState() + { + if ($this->state === null) { + $this->state = ObjectManager::getInstance()->get(State::class); + } - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); - return $resultRedirect->setPath('adminhtml/*'); + return $this->state; } } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php index 83ea91e32bf8c..ed546ba91adec 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php @@ -1,7 +1,7 @@ resultPageFactory->create(); + /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ + $resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE); + $resultPage->setActiveMenu('Magento_Backend::system_store'); + $resultPage->addBreadcrumb(__('Stores'), __('Stores')); + $resultPage->addBreadcrumb(__('All Stores'), __('All Stores')); $resultPage->getConfig()->getTitle()->prepend(__('Stores')); - return $resultPage; } } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/NewGroup.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/NewGroup.php index c9d46a1a779b4..40dd82dd8cd4b 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/NewGroup.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/NewGroup.php @@ -1,7 +1,7 @@ filterManager->removeTags($postData['website']['name']); + $websiteModel = $this->_objectManager->create(\Magento\Store\Model\Website::class); + if ($postData['website']['website_id']) { + $websiteModel->load($postData['website']['website_id']); + } + $websiteModel->setData($postData['website']); + if ($postData['website']['website_id'] == '') { + $websiteModel->setId(null); + } + + $websiteModel->save(); + $this->messageManager->addSuccess(__('You saved the website.')); + + return $postData; + } + + /** + * Process Store model save + * + * @param array $postData + * @throws \Magento\Framework\Exception\LocalizedException + * @return array + */ + private function processStoreSave($postData) + { + $eventName = 'store_edit'; + /** @var \Magento\Store\Model\Store $storeModel */ + $storeModel = $this->_objectManager->create(\Magento\Store\Model\Store::class); + $postData['store']['name'] = $this->filterManager->removeTags($postData['store']['name']); + if ($postData['store']['store_id']) { + $storeModel->load($postData['store']['store_id']); + } + $storeModel->setData($postData['store']); + if ($postData['store']['store_id'] == '') { + $storeModel->setId(null); + $eventName = 'store_add'; + } + $groupModel = $this->_objectManager->create( + \Magento\Store\Model\Group::class + )->load( + $storeModel->getGroupId() + ); + $storeModel->setWebsiteId($groupModel->getWebsiteId()); + if (!$storeModel->isActive() && $storeModel->isDefault()) { + throw new \Magento\Framework\Exception\LocalizedException( + __('The default store cannot be disabled') + ); + } + $storeModel->save(); + $this->_objectManager->get(\Magento\Store\Model\StoreManager::class)->reinitStores(); + $this->_eventManager->dispatch($eventName, ['store' => $storeModel]); + $this->messageManager->addSuccess(__('You saved the store view.')); + + return $postData; + } + + /** + * Process StoreGroup model save + * + * @param array $postData + * @throws \Magento\Framework\Exception\LocalizedException + * @return array + */ + private function processGroupSave($postData) + { + $postData['group']['name'] = $this->filterManager->removeTags($postData['group']['name']); + /** @var \Magento\Store\Model\Group $groupModel */ + $groupModel = $this->_objectManager->create(\Magento\Store\Model\Group::class); + if ($postData['group']['group_id']) { + $groupModel->load($postData['group']['group_id']); + } + $groupModel->setData($postData['group']); + if ($postData['group']['group_id'] == '') { + $groupModel->setId(null); + } + if (!$this->isSelectedDefaultStoreActive($postData, $groupModel)) { + throw new \Magento\Framework\Exception\LocalizedException( + __('An inactive store view cannot be saved as default store view') + ); + } + $groupModel->save(); + $this->_eventManager->dispatch('store_group_save', ['group' => $groupModel]); + $this->messageManager->addSuccess(__('You saved the store.')); + + return $postData; + } + /** * @return \Magento\Backend\Model\View\Result\Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -22,73 +121,16 @@ public function execute() $redirectResult->setPath('adminhtml/*/'); return $redirectResult; } - try { switch ($postData['store_type']) { case 'website': - $postData['website']['name'] = $this->filterManager->removeTags($postData['website']['name']); - $websiteModel = $this->_objectManager->create(\Magento\Store\Model\Website::class); - if ($postData['website']['website_id']) { - $websiteModel->load($postData['website']['website_id']); - } - $websiteModel->setData($postData['website']); - if ($postData['website']['website_id'] == '') { - $websiteModel->setId(null); - } - - $websiteModel->save(); - $this->messageManager->addSuccess(__('You saved the website.')); + $postData = $this->processWebsiteSave($postData); break; - case 'group': - $postData['group']['name'] = $this->filterManager->removeTags($postData['group']['name']); - /** @var \Magento\Store\Model\Group $groupModel */ - $groupModel = $this->_objectManager->create(\Magento\Store\Model\Group::class); - if ($postData['group']['group_id']) { - $groupModel->load($postData['group']['group_id']); - } - $groupModel->setData($postData['group']); - if ($postData['group']['group_id'] == '') { - $groupModel->setId(null); - } - if (!$this->isSelectedDefaultStoreActive($postData, $groupModel)) { - throw new \Magento\Framework\Exception\LocalizedException( - __('An inactive store view cannot be saved as default store view') - ); - } - $groupModel->save(); - $this->_eventManager->dispatch('store_group_save', ['group' => $groupModel]); - $this->messageManager->addSuccess(__('You saved the store.')); + $postData = $this->processGroupSave($postData); break; - case 'store': - $eventName = 'store_edit'; - /** @var \Magento\Store\Model\Store $storeModel */ - $storeModel = $this->_objectManager->create(\Magento\Store\Model\Store::class); - $postData['store']['name'] = $this->filterManager->removeTags($postData['store']['name']); - if ($postData['store']['store_id']) { - $storeModel->load($postData['store']['store_id']); - } - $storeModel->setData($postData['store']); - if ($postData['store']['store_id'] == '') { - $storeModel->setId(null); - $eventName = 'store_add'; - } - $groupModel = $this->_objectManager->create( - \Magento\Store\Model\Group::class - )->load( - $storeModel->getGroupId() - ); - $storeModel->setWebsiteId($groupModel->getWebsiteId()); - if (!$storeModel->isActive() && $storeModel->isDefault()) { - throw new \Magento\Framework\Exception\LocalizedException( - __('The default store cannot be disabled') - ); - } - $storeModel->save(); - $this->_objectManager->get(\Magento\Store\Model\StoreManager::class)->reinitStores(); - $this->_eventManager->dispatch($eventName, ['store' => $storeModel]); - $this->messageManager->addSuccess(__('You saved the store view.')); + $postData = $this->processStoreSave($postData); break; default: $redirectResult->setPath('adminhtml/*/'); diff --git a/app/code/Magento/Backend/Cron/CleanCache.php b/app/code/Magento/Backend/Cron/CleanCache.php index 840e0dcfcfde0..7dc09da13d608 100644 --- a/app/code/Magento/Backend/Cron/CleanCache.php +++ b/app/code/Magento/Backend/Cron/CleanCache.php @@ -1,6 +1,6 @@ _path = $pathInMenuStructure . '/'; } $this->_logger = $logger; $this->setIteratorClass(\Magento\Backend\Model\Menu\Iterator::class); + $this->menuItemFactory = $menuItemFactory ?: ObjectManager::getInstance() + ->create(Factory::class); + $this->serializer = $serializer ?: ObjectManager::getInstance()->create(SerializerInterface::class); } /** * Add child to menu item * - * @param \Magento\Backend\Model\Menu\Item $item + * @param Item $item * @param string $parentId * @param int $index * @return void * @throws \InvalidArgumentException */ - public function add(\Magento\Backend\Model\Menu\Item $item, $parentId = null, $index = null) + public function add(Item $item, $parentId = null, $index = null) { if ($parentId !== null) { $parentItem = $this->get($parentId); @@ -69,13 +96,13 @@ public function add(\Magento\Backend\Model\Menu\Item $item, $parentId = null, $i * Retrieve menu item by id * * @param string $itemId - * @return \Magento\Backend\Model\Menu\Item|null + * @return Item|null */ public function get($itemId) { $result = null; + /** @var Item $item */ foreach ($this as $item) { - /** @var $item \Magento\Backend\Model\Menu\Item */ if ($item->getId() == $itemId) { $result = $item; break; @@ -116,8 +143,8 @@ public function move($itemId, $toItemId, $sortIndex = null) public function remove($itemId) { $result = false; + /** @var Item $item */ foreach ($this as $key => $item) { - /** @var $item \Magento\Backend\Model\Menu\Item */ if ($item->getId() == $itemId) { unset($this[$key]); $result = true; @@ -144,8 +171,8 @@ public function remove($itemId) public function reorder($itemId, $position) { $result = false; + /** @var Item $item */ foreach ($this as $key => $item) { - /** @var $item \Magento\Backend\Model\Menu\Item */ if ($item->getId() == $itemId) { unset($this[$key]); $this->add($item, null, $position); @@ -161,10 +188,10 @@ public function reorder($itemId, $position) /** * Check whether provided item is last in list * - * @param \Magento\Backend\Model\Menu\Item $item + * @param Item $item * @return bool */ - public function isLast(\Magento\Backend\Model\Menu\Item $item) + public function isLast(Item $item) { return $this->offsetGet(max(array_keys($this->getArrayCopy())))->getId() == $item->getId(); } @@ -172,12 +199,12 @@ public function isLast(\Magento\Backend\Model\Menu\Item $item) /** * Find first menu item that user is able to access * - * @return \Magento\Backend\Model\Menu\Item|null + * @return Item|null */ public function getFirstAvailable() { $result = null; - /** @var $item \Magento\Backend\Model\Menu\Item */ + /** @var Item $item */ foreach ($this as $item) { if ($item->isAllowed() && !$item->isDisabled()) { if ($item->hasChildren()) { @@ -198,7 +225,7 @@ public function getFirstAvailable() * Get parent items by item id * * @param string $itemId - * @return \Magento\Backend\Model\Menu\Item[] + * @return Item[] */ public function getParentItems($itemId) { @@ -217,8 +244,8 @@ public function getParentItems($itemId) */ protected function _findParentItems($menu, $itemId, &$parents) { + /** @var Item $item */ foreach ($menu as $item) { - /** @var $item \Magento\Backend\Model\Menu\Item */ if ($item->getId() == $itemId) { return true; } @@ -233,16 +260,54 @@ protected function _findParentItems($menu, $itemId, &$parents) } /** - * Hack to unset logger instance which cannot be serialized + * Serialize menu * * @return string */ public function serialize() { - $logger = $this->_logger; - unset($this->_logger); - $result = parent::serialize(); - $this->_logger = $logger; - return $result; + return $this->serializer->serialize($this->toArray()); + } + + /** + * Get menu data represented as an array + * + * @return array + */ + public function toArray() + { + $data = []; + foreach ($this as $item) { + $data[] = $item->toArray(); + } + return $data; + } + + /** + * Unserialize menu + * + * @param string $serialized + * @return void + */ + public function unserialize($serialized) + { + $data = $this->serializer->unserialize($serialized); + $this->populateFromArray($data); + } + + /** + * Populate the menu with data from array + * + * @param array $data + * @return void + */ + public function populateFromArray(array $data) + { + $items = []; + foreach ($data as $itemData) { + $item = $this->menuItemFactory->create($itemData); + $items[] = $item; + } + $this->exchangeArray($items); } } diff --git a/app/code/Magento/Backend/Model/Menu/AbstractDirector.php b/app/code/Magento/Backend/Model/Menu/AbstractDirector.php index 713b45fabd927..9d08ff6a186d8 100644 --- a/app/code/Magento/Backend/Model/Menu/AbstractDirector.php +++ b/app/code/Magento/Backend/Model/Menu/AbstractDirector.php @@ -1,6 +1,6 @@ query('/config/menu/*'); diff --git a/app/code/Magento/Backend/Model/Menu/Config/Menu/Dom.php b/app/code/Magento/Backend/Model/Menu/Config/Menu/Dom.php index 37da3a0aeaf3d..5d2c6dc2cbf36 100644 --- a/app/code/Magento/Backend/Model/Menu/Config/Menu/Dom.php +++ b/app/code/Magento/Backend/Model/Menu/Config/Menu/Dom.php @@ -1,6 +1,6 @@ _validator = $validator; $this->_validator->validate($data); - $this->_moduleManager = $moduleManager; $this->_acl = $authorization; $this->_scopeConfig = $scopeConfig; $this->_menuFactory = $menuFactory; $this->_urlModel = $urlModel; - $this->_moduleName = isset($data['module']) ? $data['module'] : 'Magento_Backend'; $this->_moduleList = $moduleList; - - $this->_id = $data['id']; - $this->_title = $data['title']; - $this->_action = $this->_getArgument($data, 'action'); - $this->_resource = $this->_getArgument($data, 'resource'); - $this->_dependsOnModule = $this->_getArgument($data, 'dependsOnModule'); - $this->_dependsOnConfig = $this->_getArgument($data, 'dependsOnConfig'); - $this->_tooltip = $this->_getArgument($data, 'toolTip', ''); + $this->populateFromArray($data); } /** @@ -208,6 +208,16 @@ public function getId() return $this->_id; } + /** + * Retrieve item target + * + * @return string|null + */ + public function getTarget() + { + return $this->target; + } + /** * Check whether item has subnodes * @@ -215,13 +225,13 @@ public function getId() */ public function hasChildren() { - return !is_null($this->_submenu) && (bool)$this->_submenu->count(); + return (null !== $this->_submenu) && (bool)$this->_submenu->count(); } /** * Retrieve submenu * - * @return \Magento\Backend\Model\Menu + * @return Menu */ public function getChildren() { @@ -425,7 +435,7 @@ protected function _isModuleDependenciesAvailable() protected function _isConfigDependenciesAvailable() { if ($this->_dependsOnConfig) { - return $this->_scopeConfig->isSetFlag((string)$this->_dependsOnConfig, \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + return $this->_scopeConfig->isSetFlag((string)$this->_dependsOnConfig, ScopeInterface::SCOPE_STORE); } return true; } @@ -445,45 +455,55 @@ public function isAllowed() } /** - * @return string[] + * Get menu item data represented as an array + * + * @return array */ - public function __sleep() + public function toArray() { - if ($this->_submenu) { - $this->_serializedSubmenu = $this->_submenu->serialize(); - } return [ - '_parentId', - '_moduleName', - '_sortIndex', - '_dependsOnConfig', - '_id', - '_resource', - '_path', - '_action', - '_dependsOnModule', - '_tooltip', - '_title', - '_serializedSubmenu' + 'parent_id' => $this->_parentId, + 'module_name' => $this->_moduleName, + 'sort_index' => $this->_sortIndex, + 'depends_on_config' => $this->_dependsOnConfig, + 'id' => $this->_id, + 'resource' => $this->_resource, + 'path' => $this->_path, + 'action' => $this->_action, + 'depends_on_module' => $this->_dependsOnModule, + 'tooltip' => $this->_tooltip, + 'title' => $this->_title, + 'target' => $this->target, + 'sub_menu' => isset($this->_submenu) ? $this->_submenu->toArray() : null ]; } /** + * Populate the menu item with data from array + * + * @param array $data * @return void */ - public function __wakeup() + public function populateFromArray(array $data) { - $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); - $this->_moduleManager = $objectManager->get(\Magento\Framework\Module\Manager::class); - $this->_validator = $objectManager->get(\Magento\Backend\Model\Menu\Item\Validator::class); - $this->_acl = $objectManager->get(\Magento\Framework\AuthorizationInterface::class); - $this->_scopeConfig = $objectManager->get(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->_menuFactory = $objectManager->get(\Magento\Backend\Model\MenuFactory::class); - $this->_urlModel = $objectManager->get(\Magento\Backend\Model\UrlInterface::class); - $this->_moduleList = $objectManager->get(\Magento\Framework\Module\ModuleListInterface::class); - if ($this->_serializedSubmenu) { - $this->_submenu = $this->_menuFactory->create(); - $this->_submenu->unserialize($this->_serializedSubmenu); + $this->_parentId = $this->_getArgument($data, 'parent_id'); + $this->_moduleName = $this->_getArgument($data, 'module_name', 'Magento_Backend'); + $this->_sortIndex = $this->_getArgument($data, 'sort_index'); + $this->_dependsOnConfig = $this->_getArgument($data, 'depends_on_config'); + $this->_id = $this->_getArgument($data, 'id'); + $this->_resource = $this->_getArgument($data, 'resource'); + $this->_path = $this->_getArgument($data, 'path', ''); + $this->_action = $this->_getArgument($data, 'action'); + $this->_dependsOnModule = $this->_getArgument($data, 'depends_on_module'); + $this->_tooltip = $this->_getArgument($data, 'tooltip', ''); + $this->_title = $this->_getArgument($data, 'title'); + $this->target = $this->_getArgument($data, 'target'); + if (isset($data['sub_menu'])) { + $menu = $this->_menuFactory->create(); + $menu->populateFromArray($data['sub_menu']); + $this->_submenu = $menu; + } else { + $this->_submenu = null; } } } diff --git a/app/code/Magento/Backend/Model/Menu/Item/Factory.php b/app/code/Magento/Backend/Model/Menu/Item/Factory.php index aabb6fb704d3f..8d2a7fdf37144 100644 --- a/app/code/Magento/Backend/Model/Menu/Item/Factory.php +++ b/app/code/Magento/Backend/Model/Menu/Item/Factory.php @@ -1,6 +1,6 @@ _encryptor = $encryptor; + $hostChecker = $hostChecker ?: ObjectManager::getInstance()->get(HostChecker::class); parent::__construct( $routeConfig, $request, @@ -133,7 +139,8 @@ public function __construct( $scopeConfig, $routeParamsPreprocessor, $scopeType, - $data + $data, + $hostChecker ); $this->_backendHelper = $backendHelper; $this->_menuConfig = $menuConfig; diff --git a/app/code/Magento/Backend/Model/Url/ScopeResolver.php b/app/code/Magento/Backend/Model/Url/ScopeResolver.php index 596465415d77f..923341d4a30c8 100644 --- a/app/code/Magento/Backend/Model/Url/ScopeResolver.php +++ b/app/code/Magento/Backend/Model/Url/ScopeResolver.php @@ -1,6 +1,6 @@ + * + * + * Condition\Implementation + * + * + * + * + * Registered condition can be used by ui component declaration in layout + * + * + * + * "condition" just another optional attribute of ui component declaration + */ +interface ConditionInterface +{ + /** + * Validate logical condition for ui component + * If validation passed block will be displayed + * + * @return bool + */ + public function validate(); +} diff --git a/app/code/Magento/Backend/Model/View/Layout/ConditionPool.php b/app/code/Magento/Backend/Model/View/Layout/ConditionPool.php new file mode 100644 index 0000000000000..ff73b8c519824 --- /dev/null +++ b/app/code/Magento/Backend/Model/View/Layout/ConditionPool.php @@ -0,0 +1,60 @@ +conditions = $conditions; + $this->objectManager = $objectManager; + } + + /** + * Returns condition by name + * If unknown condition requested throws InputException + * + * @param string $name + * @return ConditionInterface + * @throws InputException + */ + public function getCondition($name) + { + if (!isset($this->conditions[$name])) { + throw new InputException(__('Cannot apply unknown condition')); + } + return $this->objectManager->get($this->conditions[$name]); + } +} diff --git a/app/code/Magento/Backend/Model/View/Layout/Filter.php b/app/code/Magento/Backend/Model/View/Layout/Filter.php new file mode 100644 index 0000000000000..ed0752d2222fc --- /dev/null +++ b/app/code/Magento/Backend/Model/View/Layout/Filter.php @@ -0,0 +1,52 @@ +filters = $filters; + } + + /** + * Filter structure elements + * + * @param ScheduledStructure $scheduledStructure + * @param Structure $structure + * @return bool + */ + public function filterElement(ScheduledStructure $scheduledStructure, Structure $structure) + { + foreach ($this->filters as $filter) { + $filter->filterElement($scheduledStructure, $structure); + } + return true; + } +} diff --git a/app/code/Magento/Backend/Model/View/Layout/Filter/Acl.php b/app/code/Magento/Backend/Model/View/Layout/Filter/Acl.php index 7303bccc93893..ac2e86efeb62e 100644 --- a/app/code/Magento/Backend/Model/View/Layout/Filter/Acl.php +++ b/app/code/Magento/Backend/Model/View/Layout/Filter/Acl.php @@ -2,15 +2,30 @@ /** * ACL block filter * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Backend\Model\View\Layout\Filter; use Magento\Framework\View\Layout\ScheduledStructure; use Magento\Framework\View\Layout\Data\Structure; +use Magento\Backend\Model\View\Layout\FilterInterface; +use Magento\Backend\Model\View\Layout\StructureManager; +use Magento\Framework\App\ObjectManager; -class Acl +/** + * Class Acl + * + * Manage visibility of ui components and blocks at the backend according to ACL resources. + * If user role has corresponding resource block will be displayed. + * + * @see usage details in \Magento\Backend\Model\View\Layout\FilterInterface description + * + * Example of declaration in layout + * + * + */ +class Acl implements FilterInterface { /** * Authorization @@ -19,6 +34,11 @@ class Acl */ protected $authorization; + /** + * @var StructureManager + */ + private $structureManager; + /** * @param \Magento\Framework\AuthorizationInterface $authorization */ @@ -27,12 +47,25 @@ public function __construct(\Magento\Framework\AuthorizationInterface $authoriza $this->authorization = $authorization; } + /** + * @return StructureManager + */ + private function getStructureManager() + { + if (!$this->structureManager) { + $this->structureManager = ObjectManager::getInstance()->get(StructureManager::class); + } + return $this->structureManager; + } + /** * Delete elements that have "acl" attribute but value is "not allowed" * In any case, the "acl" attribute will be unset * * @param ScheduledStructure $scheduledStructure * @param Structure $structure + * + * @return void */ public function filterAclElements(ScheduledStructure $scheduledStructure, Structure $structure) { @@ -61,14 +94,18 @@ protected function removeElement( $elementName, $isChild = false ) { - $elementsToRemove = array_keys($structure->getChildren($elementName)); - $scheduledStructure->unsetElement($elementName); - foreach ($elementsToRemove as $element) { - $this->removeElement($scheduledStructure, $structure, $element, true); - } - if (!$isChild) { - $structure->unsetElement($elementName); - } + $this->getStructureManager()->removeElement($scheduledStructure, $structure, $elementName, $isChild); return $this; } + + /** + * @param ScheduledStructure $scheduledStructure + * @param Structure $structure + * @return bool + */ + public function filterElement(ScheduledStructure $scheduledStructure, Structure $structure) + { + $this->filterAclElements($scheduledStructure, $structure); + return true; + } } diff --git a/app/code/Magento/Backend/Model/View/Layout/Filter/Condition.php b/app/code/Magento/Backend/Model/View/Layout/Filter/Condition.php new file mode 100644 index 0000000000000..cbb72ccb51cc1 --- /dev/null +++ b/app/code/Magento/Backend/Model/View/Layout/Filter/Condition.php @@ -0,0 +1,67 @@ +structureManager = $structureManager; + $this->conditionPool = $conditionPool; + } + + /** + * Filter structure according to declared conditions + * + * @param ScheduledStructure $scheduledStructure + * @param Structure $structure + * @return bool + */ + public function filterElement(ScheduledStructure $scheduledStructure, Structure $structure) + { + foreach ($scheduledStructure->getElements() as $name => $data) { + list(, $data) = $data; + if (isset($data['attributes']['condition']) && $data['attributes']['condition']) { + $condition = $this->conditionPool->getCondition($data['attributes']['condition']); + if (!$condition->validate()) { + $this->structureManager->removeElement($scheduledStructure, $structure, $name); + } + } + } + return true; + } +} diff --git a/app/code/Magento/Backend/Model/View/Layout/FilterInterface.php b/app/code/Magento/Backend/Model/View/Layout/FilterInterface.php new file mode 100644 index 0000000000000..a1b0a302c0f66 --- /dev/null +++ b/app/code/Magento/Backend/Model/View/Layout/FilterInterface.php @@ -0,0 +1,38 @@ + + * + * + * Magento\Backend\Model\View\Layout\Filter\Acl + * + * + * + * + */ +interface FilterInterface +{ + /** + * Filter structure element + * + * @param ScheduledStructure $scheduledStructure + * @param Structure $structure + * @return bool + */ + public function filterElement(ScheduledStructure $scheduledStructure, Structure $structure); +} diff --git a/app/code/Magento/Backend/Model/View/Layout/GeneratorPool.php b/app/code/Magento/Backend/Model/View/Layout/GeneratorPool.php index 25b5c3cc0b6c8..a34d5929d5db7 100644 --- a/app/code/Magento/Backend/Model/View/Layout/GeneratorPool.php +++ b/app/code/Magento/Backend/Model/View/Layout/GeneratorPool.php @@ -1,12 +1,13 @@ filter) { + $this->filter = ObjectManager::getInstance()->get(FilterInterface::class); + } + return $this->filter; + } + /** * Build structure that is based on scheduled structure * @@ -54,7 +71,7 @@ public function __construct( protected function buildStructure(ScheduledStructure $scheduledStructure, Structure $structure) { parent::buildStructure($scheduledStructure, $structure); - $this->aclFilter->filterAclElements($scheduledStructure, $structure); + $this->getFilter()->filterElement($scheduledStructure, $structure); return $this; } } diff --git a/app/code/Magento/Backend/Model/View/Layout/Reader/Block.php b/app/code/Magento/Backend/Model/View/Layout/Reader/Block.php index a9ff03434775d..150a0c7a82191 100644 --- a/app/code/Magento/Backend/Model/View/Layout/Reader/Block.php +++ b/app/code/Magento/Backend/Model/View/Layout/Reader/Block.php @@ -1,6 +1,6 @@ getChildren($elementName)); + $scheduledStructure->unsetElement($elementName); + foreach ($elementsToRemove as $element) { + $this->removeElement($scheduledStructure, $structure, $element, true); + } + if (!$isChild) { + $structure->unsetElement($elementName); + } + return true; + } +} diff --git a/app/code/Magento/Backend/Model/View/Page/Builder.php b/app/code/Magento/Backend/Model/View/Page/Builder.php index 71d8f8241ee91..8b0fe73604137 100644 --- a/app/code/Magento/Backend/Model/View/Page/Builder.php +++ b/app/code/Magento/Backend/Model/View/Page/Builder.php @@ -1,6 +1,6 @@ sectionPool = $this->getMock( - \Magento\Framework\App\Config\ScopePool::class, - ['getScope', 'clean'], + $this->appConfig = $this->getMock( + \Magento\Framework\App\Config::class, + ['get'], [], '', false ); - $this->model = new \Magento\Backend\App\Config($this->sectionPool); + $this->model = new \Magento\Backend\App\Config($this->appConfig); } public function testGetValue() { $expectedValue = 'some value'; $path = 'some path'; - $configData = $this->getConfigDataMock('getValue'); - $configData->expects( - $this->once() - )->method( - 'getValue' - )->with( - $this->equalTo($path) - )->will( - $this->returnValue($expectedValue) - ); - $this->sectionPool->expects( + $this->appConfig->expects( $this->once() )->method( - 'getScope' + 'get' )->with( - $this->equalTo('default'), + $this->equalTo('system'), + $this->equalTo('default/' . $path), $this->isNull() )->will( - $this->returnValue($configData) + $this->returnValue($expectedValue) ); $this->assertEquals($expectedValue, $this->model->getValue($path)); } - public function testSetValue() - { - $value = 'some value'; - $path = 'some path'; - $configData = $this->getConfigDataMock('setValue'); - $configData->expects($this->once())->method('setValue')->with($this->equalTo($path), $this->equalTo($value)); - $this->sectionPool->expects( - $this->once() - )->method( - 'getScope' - )->with( - $this->equalTo('default'), - $this->isNull() - )->will( - $this->returnValue($configData) - ); - $this->model->setValue($path, $value); - } - /** + * @param string $configPath * @param mixed $configValue * @param bool $expectedResult * @dataProvider isSetFlagDataProvider */ - public function testIsSetFlag($configValue, $expectedResult) + public function testIsSetFlag($configPath, $configValue, $expectedResult) { - $path = 'some path'; - $configData = $this->getConfigDataMock('getValue'); - $configData->expects( - $this->once() + $this->appConfig->expects( + $this->any() )->method( - 'getValue' + 'get' )->with( - $this->equalTo($path) + $this->equalTo('system'), + $this->equalTo('default/' . $configPath) )->will( $this->returnValue($configValue) ); - $this->sectionPool->expects( - $this->once() - )->method( - 'getScope' - )->with( - $this->equalTo('default'), - $this->isNull() - )->will( - $this->returnValue($configData) - ); - $this->assertEquals($expectedResult, $this->model->isSetFlag($path)); + $this->assertEquals($expectedResult, $this->model->isSetFlag($configPath)); } public function isSetFlagDataProvider() { return [ - [0, false], - [true, true], - ['0', false], - ['', false], - ['some string', true], - [1, true] + ['a', 0, false], + ['b', true, true], + ['c', '0', false], + ['d', '', false], + ['e', 'some string', true], + ['f', 1, true] ]; } diff --git a/app/code/Magento/Backend/Test/Unit/App/Response/Http/FileFactoryTest.php b/app/code/Magento/Backend/Test/Unit/App/Response/Http/FileFactoryTest.php index 25c32fcaeec0b..8a4555ffa105a 100644 --- a/app/code/Magento/Backend/Test/Unit/App/Response/Http/FileFactoryTest.php +++ b/app/code/Magento/Backend/Test/Unit/App/Response/Http/FileFactoryTest.php @@ -1,6 +1,6 @@ activeMenuItemMock = $this->getMockBuilder(Item::class) + ->disableOriginalConstructor() + ->getMock(); + $this->menuItemMock = $this->getMockBuilder(Item::class) + ->disableOriginalConstructor() + ->getMock(); + $this->menuItemCheckerMock = $this->getMockBuilder(MenuItemChecker::class) + ->disableOriginalConstructor() + ->getMock(); + $this->escaperMock = $this->getMockBuilder(Escaper::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->anchorRenderer = $this->objectManagerHelper->getObject( + AnchorRenderer::class, + [ + 'menuItemChecker' => $this->menuItemCheckerMock, + 'escaper' => $this->escaperMock + ] + ); + } + + public function testRenderAnchorLevelIsOne() + { + $title = 'Title'; + $html = 'Test html'; + $this->menuItemMock->expects($this->once())->method('getUrl')->willReturn('#'); + $this->menuItemMock->expects($this->once())->method('getTitle')->willReturn($title); + $this->escaperMock->expects($this->once())->method('escapeHtml')->with(__($title))->willReturn($html); + + $expected = '' + . '' . $html . '' + . ''; + + $this->assertEquals( + $expected, + $this->anchorRenderer->renderAnchor($this->activeMenuItemMock, $this->menuItemMock, 1) + ); + } + + /** + * @param bool $hasTarget + * @dataProvider targetDataProvider + */ + public function testRenderAnchorLevelIsNotOne($hasTarget) + { + $level = 0; + $title = 'Title'; + $html = 'Test html'; + $url = 'test/url'; + $tooltip = 'Anchor title'; + $onclick = ''; + $target = '_blank'; + $finalTarget = $hasTarget ? ('target=' . $target) : ''; + $this->menuItemMock->expects($this->any())->method('getTarget')->willReturn($hasTarget ? $target : null); + $this->menuItemMock->expects($this->once())->method('getUrl')->willReturn($url); + $this->menuItemMock->expects($this->once())->method('getTitle')->willReturn($title); + $this->escaperMock->expects($this->once())->method('escapeHtml')->with(__($title))->willReturn($html); + $this->menuItemMock->expects($this->once())->method('hasTooltip')->willReturn(true); + $this->menuItemMock->expects($this->any())->method('getTooltip')->willReturn(__($tooltip)); + $this->menuItemMock->expects($this->once())->method('hasClickCallback')->willReturn(true); + $this->menuItemMock->expects($this->once())->method('getClickCallback')->willReturn($onclick); + $this->menuItemCheckerMock->expects($this->once()) + ->method('isItemActive') + ->with($this->activeMenuItemMock, $this->menuItemMock, $level)->willReturn(true); + + $expected = '' . '' . $html + . '' . ''; + + $this->assertEquals( + $expected, + $this->anchorRenderer->renderAnchor($this->activeMenuItemMock, $this->menuItemMock, $level) + ); + } + + public function targetDataProvider() + { + return [ + 'item has target' => [true], + 'item does not have target' => [false] + ]; + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Block/Cache/AdditionalTest.php b/app/code/Magento/Backend/Test/Unit/Block/Cache/AdditionalTest.php index 7b78821bff498..395620cff9288 100644 --- a/app/code/Magento/Backend/Test/Unit/Block/Cache/AdditionalTest.php +++ b/app/code/Magento/Backend/Test/Unit/Block/Cache/AdditionalTest.php @@ -1,6 +1,6 @@ menuItemMock = $this->getMockBuilder(Item::class) + ->disableOriginalConstructor() + ->getMock(); + $this->activeMenuItemMock = $this->getMockBuilder(Item::class) + ->disableOriginalConstructor() + ->getMock(); + $this->menuItemChecker = new MenuItemChecker(); + } + + /** + * @param int $activeItemId + * @param int $itemId + * @param bool $isItem + * @param bool $expected + * @dataProvider dataProvider + */ + public function testIsItemActive($activeItemId, $itemId, $isItem, $expected) + { + $this->menuMock = $this->getMockBuilder(Menu::class) + ->disableOriginalConstructor() + ->getMock(); + $this->menuItemMock->expects($this->any())->method('getId')->willReturn($itemId); + $this->activeMenuItemMock->expects($this->any())->method('getId')->willReturn($activeItemId); + $this->menuItemMock->expects($this->any())->method('getChildren')->willReturn($this->menuMock); + $this->menuMock->expects($this->any()) + ->method('get') + ->with($activeItemId) + ->willReturn($isItem ? $this->activeMenuItemMock : null); + $this->assertEquals( + $expected, + $this->menuItemChecker->isItemActive($this->activeMenuItemMock, $this->menuItemMock, 0) + ); + } + + public function testIsItemActiveLevelNotZero() + { + $this->assertFalse( + $this->menuItemChecker->isItemActive($this->activeMenuItemMock, $this->menuItemMock, 1) + ); + } + + public function dataProvider() + { + return [ + 'outputItemEquals' => ['1', '1', false, true], + 'outputItemIsChild' => ['1', '2', true, true], + 'outputItemIsChildNull' => ['1', '2', false, false], + ]; + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Block/MenuTest.php b/app/code/Magento/Backend/Test/Unit/Block/MenuTest.php new file mode 100644 index 0000000000000..ccc0e719aaecb --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Block/MenuTest.php @@ -0,0 +1,146 @@ +activeItemMock = $this->getMockBuilder(Item::class) + ->disableOriginalConstructor() + ->getMock(); + $this->urlMock = $this->getMockBuilder(UrlInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->iteratorFactoryMock = $this->getMockBuilder(IteratorFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->authSessionMock = $this->getMockBuilder(Session::class) + ->disableOriginalConstructor() + ->getMock(); + $this->menuConfigMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->localeResolverMock = $this->getMockBuilder(ResolverInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->menuItemChecker = $this->getMockBuilder(MenuItemChecker::class) + ->disableOriginalConstructor() + ->getMock(); + $this->anchorRendererMock = $this->getMockBuilder(AnchorRenderer::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->menu = $this->objectManagerHelper->getObject( + Menu::class, + [ + 'url' => $this->urlMock, + 'iteratorFactory' => $this->iteratorFactoryMock, + 'authSession' => $this->authSessionMock, + 'menuConfig' => $this->menuConfigMock, + 'localeResolver' => $this->localeResolverMock, + 'menuItemChecker' => $this->menuItemCheckerMock, + 'anchorRenderer' => $this->anchorRendererMock + ] + ); + } + + public function testGetActiveItemModelMenuIsNotNull() + { + $this->menuModelMock = $this->getMockBuilder(MenuModel::class) + ->disableOriginalConstructor() + ->getMock(); + $this->menu->setActive($this->activeItemMock); + $this->menuConfigMock->expects($this->once())->method('getMenu')->willReturn($this->menuModelMock); + $this->menuModelMock->expects($this->once()) + ->method('get') + ->willReturn($this->activeItemMock); + + $this->assertEquals($this->activeItemMock, $this->menu->getActiveItemModel()); + } + + public function testGetActiveItemModelMenuIsNull() + { + $this->menuModelMock = $this->getMockBuilder(MenuModel::class) + ->disableOriginalConstructor() + ->getMock(); + $this->menu->setActive(null); + $this->menuConfigMock->expects($this->once())->method('getMenu')->willReturn($this->menuModelMock); + $this->menuModelMock->expects($this->once()) + ->method('get') + ->willReturn(null); + + $this->assertFalse($this->menu->getActiveItemModel()); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Block/Page/System/Config/Robots/ResetTest.php b/app/code/Magento/Backend/Test/Unit/Block/Page/System/Config/Robots/ResetTest.php index 93ee165a541df..2eba4779d91e6 100644 --- a/app/code/Magento/Backend/Test/Unit/Block/Page/System/Config/Robots/ResetTest.php +++ b/app/code/Magento/Backend/Test/Unit/Block/Page/System/Config/Robots/ResetTest.php @@ -1,6 +1,6 @@ _gridMock = $this->getMock( - \Magento\Backend\Block\Widget\Grid::class, - ['getId', 'getCollection'], - [], - '', - false - ); - $this->_gridMock->expects($this->any())->method('getId')->will($this->returnValue('test_grid')); - - $this->_layoutMock = $this->getMock( - \Magento\Framework\View\Layout::class, - ['getParentName', 'getBlock', 'helper'], - [], - '', - false, - false - ); + $this->_gridMock = $this->getMockBuilder(\Magento\Backend\Block\Widget\Grid::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->setMethods(['getId', 'getCollection']) + ->getMock(); + $this->_gridMock->expects($this->any()) + ->method('getId') + ->willReturn('test_grid'); - $this->_layoutMock->expects( - $this->any() - )->method( - 'getParentName' - )->with( - 'test_grid_massaction' - )->will( - $this->returnValue('test_grid') - ); - $this->_layoutMock->expects( - $this->any() - )->method( - 'getBlock' - )->with( - 'test_grid' - )->will( - $this->returnValue($this->_gridMock) - ); + $this->_layoutMock = $this->getMockBuilder(\Magento\Framework\View\Layout::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->setMethods(['getParentName', 'getBlock', 'helper']) + ->getMock(); + $this->_layoutMock->expects($this->any()) + ->method('getParentName') + ->with('test_grid_massaction') + ->willReturn('test_grid'); + $this->_layoutMock->expects($this->any()) + ->method('getBlock') + ->with('test_grid') + ->willReturn($this->_gridMock); + + $this->_requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); - $this->_requestMock = $this->getMock(\Magento\Framework\App\Request\Http::class, [], [], '', false); + $this->_urlModelMock = $this->getMockBuilder(\Magento\Backend\Model\Url::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); - $this->_urlModelMock = $this->getMock(\Magento\Backend\Model\Url::class, [], [], '', false); + $this->visibilityCheckerMock = $this->getMockBuilder(VisibilityChecker::class) + ->getMockForAbstractClass(); $arguments = [ 'layout' => $this->_layoutMock, 'request' => $this->_requestMock, 'urlBuilder' => $this->_urlModelMock, - 'data' => ['massaction_id_field' => 'test_id', 'massaction_id_filter' => 'test_id'], + 'data' => ['massaction_id_field' => 'test_id', 'massaction_id_filter' => 'test_id'] ]; $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -124,26 +126,24 @@ public function testMassactionDefaultValues() } /** - * @param $itemId - * @param $item + * @param string $itemId + * @param \Magento\Framework\DataObject $item * @param $expectedItem \Magento\Framework\DataObject - * @dataProvider itemsDataProvider + * @dataProvider itemsProcessingDataProvider */ public function testItemsProcessing($itemId, $item, $expectedItem) { - $this->_urlModelMock->expects( - $this->any() - )->method( - 'getBaseUrl' - )->will( - $this->returnValue('http://localhost/index.php') - ); + $this->_urlModelMock->expects($this->any()) + ->method('getBaseUrl') + ->willReturn('http://localhost/index.php'); $urlReturnValueMap = [ ['*/*/test1', [], 'http://localhost/index.php/backend/admin/test/test1'], ['*/*/test2', [], 'http://localhost/index.php/backend/admin/test/test2'], ]; - $this->_urlModelMock->expects($this->any())->method('getUrl')->will($this->returnValueMap($urlReturnValueMap)); + $this->_urlModelMock->expects($this->any()) + ->method('getUrl') + ->willReturnMap($urlReturnValueMap); $this->_block->addItem($itemId, $item); $this->assertEquals(1, $this->_block->getCount()); @@ -157,7 +157,10 @@ public function testItemsProcessing($itemId, $item, $expectedItem) $this->assertNull($this->_block->getItem($itemId)); } - public function itemsDataProvider() + /** + * @return array + */ + public function itemsProcessingDataProvider() { return [ [ @@ -186,22 +189,17 @@ public function itemsDataProvider() } /** - * @param $param - * @param $expectedJson - * @param $expected + * @param string $param + * @param string $expectedJson + * @param array $expected * @dataProvider selectedDataProvider */ public function testSelected($param, $expectedJson, $expected) { - $this->_requestMock->expects( - $this->any() - )->method( - 'getParam' - )->with( - $this->_block->getFormFieldNameInternal() - )->will( - $this->returnValue($param) - ); + $this->_requestMock->expects($this->any()) + ->method('getParam') + ->with($this->_block->getFormFieldNameInternal()) + ->willReturn($param); $this->assertEquals($expectedJson, $this->_block->getSelectedJson()); $this->assertEquals($expected, $this->_block->getSelected()); @@ -262,6 +260,9 @@ public function testGetGridIdsJsonWithUseSelectAll(array $items, $result) $this->assertEquals($result, $this->_block->getGridIdsJson()); } + /** + * @return array + */ public function dataProviderGetGridIdsJsonWithUseSelectAll() { return [ @@ -279,4 +280,71 @@ public function dataProviderGetGridIdsJsonWithUseSelectAll() ], ]; } + + /** + * @param string $itemId + * @param array|\Magento\Framework\DataObject $item + * @param int $count + * @param bool $withVisibilityChecker + * @param bool $isVisible + * @dataProvider addItemDataProvider + */ + public function testAddItem($itemId, $item, $count, $withVisibilityChecker, $isVisible) + { + $this->visibilityCheckerMock->expects($this->any()) + ->method('isVisible') + ->willReturn($isVisible); + + if ($withVisibilityChecker) { + $item['visible'] = $this->visibilityCheckerMock; + } + + $urlReturnValueMap = [ + ['*/*/test1', [], 'http://localhost/index.php/backend/admin/test/test1'], + ['*/*/test2', [], 'http://localhost/index.php/backend/admin/test/test2'], + ]; + $this->_urlModelMock->expects($this->any()) + ->method('getUrl') + ->willReturnMap($urlReturnValueMap); + + $this->_block->addItem($itemId, $item); + $this->assertEquals($count, $this->_block->getCount()); + } + + /** + * @return array + */ + public function addItemDataProvider() + { + return [ + [ + 'itemId' => 'test1', + 'item' => ['label' => 'Test 1', 'url' => '*/*/test1'], + 'count' => 1, + 'withVisibilityChecker' => false, + '$isVisible' => false, + ], + [ + 'itemId' => 'test2', + 'item' => ['label' => 'Test 2', 'url' => '*/*/test2'], + 'count' => 1, + 'withVisibilityChecker' => false, + 'isVisible' => true, + ], + [ + 'itemId' => 'test1', + 'item' => ['label' => 'Test 1. Hide', 'url' => '*/*/test1'], + 'count' => 0, + 'withVisibilityChecker' => true, + 'isVisible' => false, + ], + [ + 'itemId' => 'test2', + 'item' => ['label' => 'Test 2. Does not hide', 'url' => '*/*/test2'], + 'count' => 1, + 'withVisibilityChecker' => true, + 'isVisible' => true, + ] + ]; + } } diff --git a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/SerializerTest.php b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/SerializerTest.php index 1d7caa432e59e..627b6dbec4350 100644 --- a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/SerializerTest.php +++ b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/SerializerTest.php @@ -1,6 +1,6 @@ stateMock = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); + + $this->messageManagerMock = $this->getMockBuilder(MessageManager::class) + ->getMockForAbstractClass(); + + $this->requestMock = $this->getMockBuilder(Request::class) + ->getMockForAbstractClass(); + + $this->cacheTypeListMock = $this->getMockBuilder(CacheTypeList::class) + ->getMockForAbstractClass(); + + $this->cacheStateMock = $this->getMockBuilder(CacheState::class) + ->getMockForAbstractClass(); + + $this->redirectMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); + $this->redirectMock->expects($this->once()) + ->method('setPath') + ->with('adminhtml/*') + ->willReturnSelf(); + $resultFactoryMock = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); + $resultFactoryMock->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_REDIRECT) + ->willReturn($this->redirectMock); + + $contextMock = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); + $contextMock->expects($this->once()) + ->method('getMessageManager') + ->willReturn($this->messageManagerMock); + $contextMock->expects($this->once()) + ->method('getResultFactory') + ->willReturn($resultFactoryMock); + $contextMock->expects($this->once()) + ->method('getRequest') + ->willReturn($this->requestMock); + + $this->controller = $objectManagerHelper->getObject( + MassDisable::class, + [ + 'context' => $contextMock, + 'cacheTypeList' => $this->cacheTypeListMock, + 'cacheState' => $this->cacheStateMock + ] + ); + $objectManagerHelper->setBackwardCompatibleProperty($this->controller, 'state', $this->stateMock); + } + + public function testExecuteInProductionMode() + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_PRODUCTION); + + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with('You can\'t change status of cache type(s) in production mode', null) + ->willReturnSelf(); + + $this->assertSame($this->redirectMock, $this->controller->execute()); + } + + public function testExecuteInvalidTypeCache() + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + + $this->cacheTypeListMock->expects($this->once()) + ->method('getTypes') + ->willReturn([ + 'pageCache' => [ + 'id' => 'pageCache', + 'label' => 'Cache of Page' + ] + ]); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('types') + ->willReturn(['someCache']); + + $this->messageManagerMock->expects($this->once()) + ->method('addError') + ->with('Specified cache type(s) don\'t exist: someCache') + ->willReturnSelf(); + + $this->assertSame($this->redirectMock, $this->controller->execute()); + } + + public function testExecuteWithException() + { + $exception = new \Exception(); + + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->willThrowException($exception); + + $this->messageManagerMock->expects($this->once()) + ->method('addException') + ->with($exception, 'An error occurred while disabling cache.') + ->willReturnSelf(); + + $this->assertSame($this->redirectMock, $this->controller->execute()); + } + + public function testExecuteSuccess() + { + $cacheType = 'pageCache'; + + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + + $this->cacheTypeListMock->expects($this->once()) + ->method('getTypes') + ->willReturn([ + 'pageCache' => [ + 'id' => 'pageCache', + 'label' => 'Cache of Page' + ] + ]); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('types') + ->willReturn([$cacheType]); + + $this->cacheStateMock->expects($this->once()) + ->method('isEnabled') + ->with($cacheType) + ->willReturn(true); + $this->cacheStateMock->expects($this->once()) + ->method('setEnabled') + ->with($cacheType, false); + $this->cacheStateMock->expects($this->once()) + ->method('persist'); + + $this->messageManagerMock->expects($this->once()) + ->method('addSuccess') + ->with('1 cache type(s) disabled.') + ->willReturnSelf(); + + $this->assertSame($this->redirectMock, $this->controller->execute()); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php new file mode 100644 index 0000000000000..aff391e033209 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php @@ -0,0 +1,224 @@ +stateMock = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); + + $this->messageManagerMock = $this->getMockBuilder(MessageManager::class) + ->getMockForAbstractClass(); + + $this->requestMock = $this->getMockBuilder(Request::class) + ->getMockForAbstractClass(); + + $this->cacheTypeListMock = $this->getMockBuilder(CacheTypeList::class) + ->getMockForAbstractClass(); + + $this->cacheStateMock = $this->getMockBuilder(CacheState::class) + ->getMockForAbstractClass(); + + $this->redirectMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); + $this->redirectMock->expects($this->once()) + ->method('setPath') + ->with('adminhtml/*') + ->willReturnSelf(); + $resultFactoryMock = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); + $resultFactoryMock->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_REDIRECT) + ->willReturn($this->redirectMock); + + $contextMock = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->getMock(); + $contextMock->expects($this->once()) + ->method('getMessageManager') + ->willReturn($this->messageManagerMock); + $contextMock->expects($this->once()) + ->method('getResultFactory') + ->willReturn($resultFactoryMock); + $contextMock->expects($this->once()) + ->method('getRequest') + ->willReturn($this->requestMock); + + $this->controller = $objectManagerHelper->getObject( + MassEnable::class, + [ + 'context' => $contextMock, + 'cacheTypeList' => $this->cacheTypeListMock, + 'cacheState' => $this->cacheStateMock + ] + ); + $objectManagerHelper->setBackwardCompatibleProperty($this->controller, 'state', $this->stateMock); + } + + public function testExecuteInProductionMode() + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_PRODUCTION); + + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with('You can\'t change status of cache type(s) in production mode', null) + ->willReturnSelf(); + + $this->assertSame($this->redirectMock, $this->controller->execute()); + } + + public function testExecuteInvalidTypeCache() + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + + $this->cacheTypeListMock->expects($this->once()) + ->method('getTypes') + ->willReturn([ + 'pageCache' => [ + 'id' => 'pageCache', + 'label' => 'Cache of Page' + ] + ]); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('types') + ->willReturn(['someCache']); + + $this->messageManagerMock->expects($this->once()) + ->method('addError') + ->with('Specified cache type(s) don\'t exist: someCache') + ->willReturnSelf(); + + $this->assertSame($this->redirectMock, $this->controller->execute()); + } + + public function testExecuteWithException() + { + $exception = new \Exception(); + + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->willThrowException($exception); + + $this->messageManagerMock->expects($this->once()) + ->method('addException') + ->with($exception, 'An error occurred while enabling cache.') + ->willReturnSelf(); + + $this->assertSame($this->redirectMock, $this->controller->execute()); + } + + public function testExecuteSuccess() + { + $cacheType = 'pageCache'; + + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + + $this->cacheTypeListMock->expects($this->once()) + ->method('getTypes') + ->willReturn([ + 'pageCache' => [ + 'id' => 'pageCache', + 'label' => 'Cache of Page' + ] + ]); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('types') + ->willReturn([$cacheType]); + + $this->cacheStateMock->expects($this->once()) + ->method('isEnabled') + ->with($cacheType) + ->willReturn(false); + $this->cacheStateMock->expects($this->once()) + ->method('setEnabled') + ->with($cacheType, true); + $this->cacheStateMock->expects($this->once()) + ->method('persist'); + + $this->messageManagerMock->expects($this->once()) + ->method('addSuccess') + ->with('1 cache type(s) enabled.') + ->willReturnSelf(); + + $this->assertSame($this->redirectMock, $this->controller->execute()); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/AbstractTestCase.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/AbstractTestCase.php index 43143f0587ee7..16e109b516410 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/AbstractTestCase.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/AbstractTestCase.php @@ -1,6 +1,6 @@ resultFactoryMock = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->pageMock = $this->getMockBuilder(Page::class) + ->disableOriginalConstructor() + ->getMock(); + $this->pageConfigMock = $this->getMockBuilder(Config::class) + ->setMethods(['getTitle']) + ->disableOriginalConstructor() + ->getMock(); + $this->titleMock = $this->getMockBuilder(Title::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->indexController = $this->objectManagerHelper->getObject( + Index::class, + [ + 'resultFactory' => $this->resultFactoryMock + ] + ); + } + + public function testIndex() + { + $this->resultFactoryMock->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_PAGE) + ->willReturn($this->pageMock); + $this->pageMock->expects($this->once()) + ->method('setActiveMenu') + ->with('Magento_Backend::system_store') + ->willReturnSelf(); + $this->pageMock->expects($this->exactly(2)) + ->method('addBreadcrumb') + ->withConsecutive( + [__('Stores'), __('Stores')], + [__('All Stores'), __('All Stores')] + ); + $this->pageMock->expects($this->once()) + ->method('getConfig') + ->willReturn($this->pageConfigMock); + $this->pageConfigMock->expects($this->once())->method('getTitle')->willReturn($this->titleMock); + $this->titleMock->expects($this->once())->method('prepend')->with(__('Stores'))->willReturn($this->pageMock); + + $this->assertSame($this->pageMock, $this->indexController->execute()); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Cron/CleanCacheTest.php b/app/code/Magento/Backend/Test/Unit/Cron/CleanCacheTest.php index 4386e7c395300..7e8fbfe197bf9 100644 --- a/app/code/Magento/Backend/Test/Unit/Cron/CleanCacheTest.php +++ b/app/code/Magento/Backend/Test/Unit/Cron/CleanCacheTest.php @@ -1,6 +1,6 @@ _factoryMock = $this->getMock(\Magento\Backend\Model\Menu\Item\Factory::class, [], [], '', false); - $this->_menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] + $this->factoryMock = $this->getMock(\Magento\Backend\Model\Menu\Item\Factory::class, [], [], '', false); + $this->menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); + + $this->model = (new ObjectManager($this))->getObject( + \Magento\Backend\Model\Menu\Builder::class, + [ + 'menuItemFactory' => $this->factoryMock + ] ); - - $this->_model = new \Magento\Backend\Model\Menu\Builder($this->_factoryMock, $this->_menuMock); } public function testProcessCommand() @@ -41,20 +44,20 @@ public function testProcessCommand() $command2 = $this->getMock(\Magento\Backend\Model\Menu\Builder\Command\Update::class, [], [], '', false); $command2->expects($this->any())->method('getId')->will($this->returnValue(1)); $command->expects($this->once())->method('chain')->with($this->equalTo($command2)); - $this->_model->processCommand($command); - $this->_model->processCommand($command2); + $this->model->processCommand($command); + $this->model->processCommand($command2); } public function testGetResultBuildsTreeStructure() { $item1 = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $item1->expects($this->once())->method('getChildren')->will($this->returnValue($this->_menuMock)); - $this->_factoryMock->expects($this->any())->method('create')->will($this->returnValue($item1)); + $item1->expects($this->once())->method('getChildren')->will($this->returnValue($this->menuMock)); + $this->factoryMock->expects($this->any())->method('create')->will($this->returnValue($item1)); $item2 = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_factoryMock->expects($this->at(1))->method('create')->will($this->returnValue($item2)); + $this->factoryMock->expects($this->at(1))->method('create')->will($this->returnValue($item2)); - $this->_menuMock->expects( + $this->menuMock->expects( $this->at(0) )->method( 'add' @@ -64,7 +67,7 @@ public function testGetResultBuildsTreeStructure() $this->equalTo(2) ); - $this->_menuMock->expects( + $this->menuMock->expects( $this->at(1) )->method( 'add' @@ -74,7 +77,7 @@ public function testGetResultBuildsTreeStructure() $this->equalTo(4) ); - $this->_model->processCommand( + $this->model->processCommand( new \Magento\Backend\Model\Menu\Builder\Command\Add( [ 'id' => 'item1', @@ -85,7 +88,7 @@ public function testGetResultBuildsTreeStructure() ] ) ); - $this->_model->processCommand( + $this->model->processCommand( new \Magento\Backend\Model\Menu\Builder\Command\Add( [ 'id' => 'item2', @@ -98,12 +101,12 @@ public function testGetResultBuildsTreeStructure() ) ); - $this->_model->getResult($this->_menuMock); + $this->model->getResult($this->menuMock); } public function testGetResultSkipsRemovedItems() { - $this->_model->processCommand( + $this->model->processCommand( new \Magento\Backend\Model\Menu\Builder\Command\Add( [ 'id' => 1, @@ -113,11 +116,11 @@ public function testGetResultSkipsRemovedItems() ] ) ); - $this->_model->processCommand(new \Magento\Backend\Model\Menu\Builder\Command\Remove(['id' => 1])); + $this->model->processCommand(new \Magento\Backend\Model\Menu\Builder\Command\Remove(['id' => 1])); - $this->_menuMock->expects($this->never())->method('addChild'); + $this->menuMock->expects($this->never())->method('addChild'); - $this->_model->getResult($this->_menuMock); + $this->model->getResult($this->menuMock); } /** @@ -126,9 +129,9 @@ public function testGetResultSkipsRemovedItems() public function testGetResultSkipItemsWithInvalidParent() { $item1 = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_factoryMock->expects($this->any())->method('create')->will($this->returnValue($item1)); + $this->factoryMock->expects($this->any())->method('create')->will($this->returnValue($item1)); - $this->_model->processCommand( + $this->model->processCommand( new \Magento\Backend\Model\Menu\Builder\Command\Add( [ 'id' => 'item1', @@ -140,6 +143,6 @@ public function testGetResultSkipItemsWithInvalidParent() ) ); - $this->_model->getResult($this->_menuMock); + $this->model->getResult($this->menuMock); } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/ConverterTest.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/ConverterTest.php index a05526c715815..e60f32218138b 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/ConverterTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/ConverterTest.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/valid_menu.xml b/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/valid_menu.xml index 4627fb49a031f..d60f5a0cd37da 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/valid_menu.xml +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/valid_menu.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/ConfigTest.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/ConfigTest.php index dee7518be3ac9..424cf696601ba 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/ConfigTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/ConfigTest.php @@ -1,63 +1,47 @@ _cacheInstanceMock = $this->getMock( + $this->cacheInstanceMock = $this->getMock( \Magento\Framework\App\Cache\Type\Config::class, [], [], @@ -65,15 +49,7 @@ protected function setUp() false ); - $this->_directorMock = $this->getMock( - \Magento\Backend\Model\Menu\AbstractDirector::class, - [], - [], - '', - false - ); - - $this->_menuFactoryMock = $this->getMock( + $menuFactoryMock = $this->getMock( \Magento\Backend\Model\MenuFactory::class, ['create'], [], @@ -81,7 +57,7 @@ protected function setUp() false ); - $this->_configReaderMock = $this->getMock( + $this->configReaderMock = $this->getMock( \Magento\Backend\Model\Menu\Config\Reader::class, [], [], @@ -89,30 +65,15 @@ protected function setUp() false ); - $this->_eventManagerMock = $this->getMock( - \Magento\Framework\Event\ManagerInterface::class, - [], - [], - '', - false, - false - ); - - $this->_logger = $this->getMock(\Psr\Log\LoggerInterface::class); - - $this->_menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $this->logger = $this->getMock(\Psr\Log\LoggerInterface::class); - $this->_menuBuilderMock = $this->getMock(\Magento\Backend\Model\Menu\Builder::class, [], [], '', false); + $this->menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); - $this->_menuFactoryMock->expects($this->any())->method('create')->will($this->returnValue($this->_menuMock)); + $this->menuBuilderMock = $this->getMock(\Magento\Backend\Model\Menu\Builder::class, [], [], '', false); - $scopeConfig = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $menuFactoryMock->expects($this->any())->method('create')->will($this->returnValue($this->menuMock)); - $this->_configReaderMock->expects($this->any())->method('read')->will($this->returnValue([])); + $this->configReaderMock->expects($this->any())->method('read')->will($this->returnValue([])); $appState = $this->getMock(\Magento\Framework\App\State::class, ['getAreaCode'], [], '', false); $appState->expects( @@ -123,22 +84,22 @@ protected function setUp() $this->returnValue(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) ); - $this->_model = new \Magento\Backend\Model\Menu\Config( - $this->_menuBuilderMock, - $this->_directorMock, - $this->_menuFactoryMock, - $this->_configReaderMock, - $this->_cacheInstanceMock, - $this->_eventManagerMock, - $this->_logger, - $scopeConfig, - $appState + $this->model = (new ObjectManager($this))->getObject( + \Magento\Backend\Model\Menu\Config::class, + [ + 'menuBuilder' => $this->menuBuilderMock, + 'menuFactory' => $menuFactoryMock, + 'configReader' => $this->configReaderMock, + 'configCacheType' => $this->cacheInstanceMock, + 'logger' => $this->logger, + 'appState' => $appState, + ] ); } public function testGetMenuWithCachedObjectReturnsUnserializedObject() { - $this->_cacheInstanceMock->expects( + $this->cacheInstanceMock->expects( $this->once() )->method( 'load' @@ -148,14 +109,14 @@ public function testGetMenuWithCachedObjectReturnsUnserializedObject() $this->returnValue('menu_cache') ); - $this->_menuMock->expects($this->once())->method('unserialize')->with('menu_cache'); + $this->menuMock->expects($this->once())->method('unserialize')->with('menu_cache'); - $this->assertEquals($this->_menuMock, $this->_model->getMenu()); + $this->assertEquals($this->menuMock, $this->model->getMenu()); } public function testGetMenuWithNotCachedObjectBuidlsObject() { - $this->_cacheInstanceMock->expects( + $this->cacheInstanceMock->expects( $this->at(0) )->method( 'load' @@ -165,17 +126,17 @@ public function testGetMenuWithNotCachedObjectBuidlsObject() $this->returnValue(false) ); - $this->_configReaderMock->expects($this->once())->method('read')->will($this->returnValue([])); + $this->configReaderMock->expects($this->once())->method('read')->will($this->returnValue([])); - $this->_menuBuilderMock->expects( + $this->menuBuilderMock->expects( $this->exactly(1) )->method( 'getResult' )->will( - $this->returnValue($this->_menuMock) + $this->returnValue($this->menuMock) ); - $this->assertEquals($this->_menuMock, $this->_model->getMenu()); + $this->assertEquals($this->menuMock, $this->model->getMenu()); } /** @@ -186,7 +147,7 @@ public function testGetMenuWithNotCachedObjectBuidlsObject() public function testGetMenuExceptionLogged($expectedException) { $this->setExpectedException($expectedException); - $this->_menuBuilderMock->expects( + $this->menuBuilderMock->expects( $this->exactly(1) )->method( 'getResult' @@ -194,7 +155,7 @@ public function testGetMenuExceptionLogged($expectedException) $this->throwException(new $expectedException()) ); - $this->_model->getMenu(); + $this->model->getMenu(); } public function getMenuExceptionLoggedDataProvider() @@ -208,9 +169,9 @@ public function getMenuExceptionLoggedDataProvider() public function testGetMenuGenericExceptionIsNotLogged() { - $this->_logger->expects($this->never())->method('critical'); + $this->logger->expects($this->never())->method('critical'); - $this->_menuBuilderMock->expects( + $this->menuBuilderMock->expects( $this->exactly(1) )->method( 'getResult' @@ -218,7 +179,7 @@ public function testGetMenuGenericExceptionIsNotLogged() $this->throwException(new \Exception()) ); try { - $this->_model->getMenu(); + $this->model->getMenu(); } catch (\Exception $e) { return; } diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/Director/DirectorTest.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/Director/DirectorTest.php index 550a0a4354832..f4b40aa20fb30 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/Director/DirectorTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/Director/DirectorTest.php @@ -1,6 +1,6 @@ _items['item1'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_items['item1']->expects($this->any())->method('getId')->will($this->returnValue('item1')); - $this->_items['item1']->expects($this->any())->method('isDisabled')->will($this->returnValue(false)); - $this->_items['item1']->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); - - $this->_items['item2'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_items['item2']->expects($this->any())->method('getId')->will($this->returnValue('item2')); - $this->_items['item2']->expects($this->any())->method('isDisabled')->will($this->returnValue(true)); - $this->_items['item2']->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); - - $this->_items['item3'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); - $this->_items['item3']->expects($this->any())->method('getId')->will($this->returnValue('item3')); - $this->_items['item3']->expects($this->any())->method('isDisabled')->will($this->returnValue(false)); - $this->_items['item3']->expects($this->any())->method('isAllowed')->will($this->returnValue(false)); - - $loggerMock = $this->getMock(\Psr\Log\LoggerInterface::class); - - $this->_menuModel = new \Magento\Backend\Model\Menu($loggerMock); + $this->items['item1'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); + $this->items['item1']->expects($this->any())->method('getId')->will($this->returnValue('item1')); + $this->items['item1']->expects($this->any())->method('isDisabled')->will($this->returnValue(false)); + $this->items['item1']->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); + + $this->items['item2'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); + $this->items['item2']->expects($this->any())->method('getId')->will($this->returnValue('item2')); + $this->items['item2']->expects($this->any())->method('isDisabled')->will($this->returnValue(true)); + $this->items['item2']->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); + + $this->items['item3'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); + $this->items['item3']->expects($this->any())->method('getId')->will($this->returnValue('item3')); + $this->items['item3']->expects($this->any())->method('isDisabled')->will($this->returnValue(false)); + $this->items['item3']->expects($this->any())->method('isAllowed')->will($this->returnValue(false)); + + $this->menuModel = (new ObjectManager($this))->getObject(\Magento\Backend\Model\Menu::class); } public function testLoopWithAllItemsDisabledDoesntIterate() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; @@ -59,15 +59,15 @@ public function testLoopWithAllItemsDisabledDoesntIterate() public function testLoopIteratesOnlyValidItems() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->_items['item1']); + $this->menuModel->add($this->items['item1']); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; @@ -79,16 +79,16 @@ public function testLoopIteratesOnlyValidItems() public function testLoopIteratesDosntIterateDisabledItems() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->_items['item1']); - $this->_menuModel->add($this->_items['item2']); + $this->menuModel->add($this->items['item1']); + $this->menuModel->add($this->items['item2']); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; @@ -100,16 +100,16 @@ public function testLoopIteratesDosntIterateDisabledItems() public function testLoopIteratesDosntIterateNotAllowedItems() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->_items['item1']); - $this->_menuModel->add($this->_items['item3']); + $this->menuModel->add($this->items['item1']); + $this->menuModel->add($this->items['item3']); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; @@ -121,17 +121,17 @@ public function testLoopIteratesDosntIterateNotAllowedItems() public function testLoopIteratesMixedItems() { - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->_items['item1']); - $this->_menuModel->add($this->_items['item2']); - $this->_menuModel->add($this->_items['item3']); + $this->menuModel->add($this->items['item1']); + $this->menuModel->add($this->items['item2']); + $this->menuModel->add($this->items['item3']); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); - $this->_menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); + $this->menuModel->add($this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false)); $filterIteratorModel = new \Magento\Backend\Model\Menu\Filter\Iterator( - $this->_menuModel->getIterator() + $this->menuModel->getIterator() ); $items = []; diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/Item/ValidatorTest.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/Item/ValidatorTest.php index 5d7e050d32468..0391c91fa6c0e 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/Item/ValidatorTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/Item/ValidatorTest.php @@ -1,6 +1,6 @@ 'Item Title', 'action' => '/system/config', 'resource' => 'Magento_Config::config', - 'dependsOnModule' => 'Magento_Backend', - 'dependsOnConfig' => 'system/config/isEnabled', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', 'tooltip' => 'Item tooltip', ]; @@ -76,15 +74,15 @@ protected function setUp() ); $this->_urlModelMock = $this->getMock(\Magento\Backend\Model\Url::class, [], [], '', false); $this->_moduleManager = $this->getMock(\Magento\Framework\Module\Manager::class, [], [], '', false); - $this->_validatorMock = $this->getMock(\Magento\Backend\Model\Menu\Item\Validator::class); - $this->_validatorMock->expects($this->any())->method('validate'); + $validatorMock = $this->getMock(\Magento\Backend\Model\Menu\Item\Validator::class); + $validatorMock->expects($this->any())->method('validate'); $this->_moduleListMock = $this->getMock(\Magento\Framework\Module\ModuleListInterface::class); - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_model = $helper->getObject( + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->_model = $this->objectManager->getObject( \Magento\Backend\Model\Menu\Item::class, [ - 'validator' => $this->_validatorMock, + 'validator' => $validatorMock, 'authorization' => $this->_aclMock, 'scopeConfig' => $this->_scopeConfigMock, 'menuFactory' => $this->_menuFactoryMock, @@ -99,8 +97,7 @@ protected function setUp() public function testGetUrlWithEmptyActionReturnsHashSign() { $this->_params['action'] = ''; - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $item = $helper->getObject( + $item = $this->objectManager->getObject( \Magento\Backend\Model\Menu\Item::class, ['menuFactory' => $this->_menuFactoryMock, 'data' => $this->_params] ); @@ -129,8 +126,7 @@ public function testHasClickCallbackReturnsFalseIfItemHasAction() public function testHasClickCallbackReturnsTrueIfItemHasNoAction() { $this->_params['action'] = ''; - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $item = $helper->getObject( + $item = $this->objectManager->getObject( \Magento\Backend\Model\Menu\Item::class, ['menuFactory' => $this->_menuFactoryMock, 'data' => $this->_params] ); @@ -140,8 +136,7 @@ public function testHasClickCallbackReturnsTrueIfItemHasNoAction() public function testGetClickCallbackReturnsStoppingJsIfItemDoesntHaveAction() { $this->_params['action'] = ''; - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $item = $helper->getObject( + $item = $this->objectManager->getObject( \Magento\Backend\Model\Menu\Item::class, ['menuFactory' => $this->_menuFactoryMock, 'data' => $this->_params] ); @@ -218,15 +213,86 @@ public function testIsAllowedReturnsFalseIfResourceIsNotAvailable() public function testGetChildrenCreatesSubmenuOnFirstCall() { - $menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); $this->_menuFactoryMock->expects($this->once())->method('create')->will($this->returnValue($menuMock)); $this->_model->getChildren(); $this->_model->getChildren(); } + + /** + * @param array $data + * @param array $expected + * @dataProvider toArrayDataProvider + */ + public function testToArray(array $data, array $expected) + { + $menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); + $this->_menuFactoryMock->method('create')->will($this->returnValue($menuMock)); + $menuMock->method('toArray') + ->willReturn($data['sub_menu']); + + $model = $this->objectManager->getObject( + \Magento\Backend\Model\Menu\Item::class, + [ + 'authorization' => $this->_aclMock, + 'scopeConfig' => $this->_scopeConfigMock, + 'menuFactory' => $this->_menuFactoryMock, + 'urlModel' => $this->_urlModelMock, + 'moduleList' => $this->_moduleListMock, + 'moduleManager' => $this->_moduleManager, + 'data' => $data + ] + ); + $this->assertEquals($expected, $model->toArray()); + } + + /** + * @return array + */ + public function toArrayDataProvider() + { + return include __DIR__ . '/../_files/menu_item_data.php'; + } + + /** + * @param array $constructorData + * @param array $populateFromData + * @param array $expected + * @dataProvider populateFromArrayDataProvider + */ + public function testPopulateFromArray( + array $constructorData, + array $populateFromData, + array $expected + ) { + $menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); + $this->_menuFactoryMock->method('create')->willReturn($menuMock); + $menuMock->method('toArray') + ->willReturn(['submenuArray']); + + $model = $this->objectManager->getObject( + \Magento\Backend\Model\Menu\Item::class, + [ + 'authorization' => $this->_aclMock, + 'scopeConfig' => $this->_scopeConfigMock, + 'menuFactory' => $this->_menuFactoryMock, + 'urlModel' => $this->_urlModelMock, + 'moduleList' => $this->_moduleListMock, + 'moduleManager' => $this->_moduleManager, + 'data' => $constructorData + ] + ); + $model->populateFromArray($populateFromData); + $this->assertEquals($expected, $model->toArray()); + } + + /** + * @return array + */ + public function populateFromArrayDataProvider() + { + return include __DIR__ . '/../_files/menu_item_constructor_data.php'; + } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/MenuBuilderTest.php b/app/code/Magento/Backend/Test/Unit/Model/MenuBuilderTest.php index fc519ded8e860..7d972aa2792e1 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/MenuBuilderTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/MenuBuilderTest.php @@ -1,6 +1,6 @@ objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->_items['item1'] = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); $this->_items['item1']->expects($this->any())->method('getId')->will($this->returnValue('item1')); @@ -35,7 +45,12 @@ protected function setUp() $this->_logger = $this->getMock(\Psr\Log\LoggerInterface::class); - $this->_model = new \Magento\Backend\Model\Menu($this->_logger); + $this->_model = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger + ] + ); } public function testAdd() @@ -53,7 +68,7 @@ public function testAddDoLogAddAction() public function testAddToItem() { - $subMenu = $this->getMock(\Magento\Backend\Model\Menu::class, [], [$this->_logger]); + $subMenu = $this->getMockBuilder(\Magento\Backend\Model\Menu::class)->disableOriginalConstructor()->getMock(); $subMenu->expects($this->once())->method("add")->with($this->_items['item2']); $this->_items['item1']->expects($this->once())->method("getChildren")->will($this->returnValue($subMenu)); @@ -101,19 +116,29 @@ public function testGet() public function testGetRecursive() { - $menu1 = new \Magento\Backend\Model\Menu($this->_logger); - $menu2 = new \Magento\Backend\Model\Menu($this->_logger); + $menuOne = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger + ] + ); + $menuTwo = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger + ] + ); $this->_items['item1']->expects($this->any())->method('hasChildren')->will($this->returnValue(true)); - $this->_items['item1']->expects($this->any())->method('getChildren')->will($this->returnValue($menu1)); + $this->_items['item1']->expects($this->any())->method('getChildren')->will($this->returnValue($menuOne)); $this->_model->add($this->_items['item1']); $this->_items['item2']->expects($this->any())->method('hasChildren')->will($this->returnValue(true)); - $this->_items['item2']->expects($this->any())->method('getChildren')->will($this->returnValue($menu2)); - $menu1->add($this->_items['item2']); + $this->_items['item2']->expects($this->any())->method('getChildren')->will($this->returnValue($menuTwo)); + $menuOne->add($this->_items['item2']); $this->_items['item3']->expects($this->any())->method('hasChildren')->will($this->returnValue(false)); - $menu2->add($this->_items['item3']); + $menuTwo->add($this->_items['item3']); $this->assertEquals($this->_items['item1'], $this->_model->get('item1')); $this->assertEquals($this->_items['item2'], $this->_model->get('item2')); @@ -126,11 +151,7 @@ public function testMove() $this->_model->add($this->_items['item2']); $this->_model->add($this->_items['item3']); - $subMenu = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $subMenu = $this->getMockBuilder(\Magento\Backend\Model\Menu::class)->disableOriginalConstructor()->getMock(); $subMenu->expects($this->once())->method("add")->with($this->_items['item3']); $this->_items['item1']->expects($this->once())->method("getChildren")->will($this->returnValue($subMenu)); @@ -179,11 +200,7 @@ public function testRemoveRemovesMenuItem() public function testRemoveRemovesMenuItemRecursively() { - $menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $menuMock = $this->getMockBuilder(\Magento\Backend\Model\Menu::class)->disableOriginalConstructor()->getMock(); $menuMock->expects($this->once())->method('remove')->with($this->equalTo('item2')); $this->_items['item1']->expects($this->any())->method('hasChildren')->will($this->returnValue(true)); @@ -214,7 +231,12 @@ public function testReorderReordersItemOnItsLevel() { $this->_logger->expects($this->any())->method('log'); - $subMenu = new \Magento\Backend\Model\Menu($this->_logger); + $subMenu = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger + ] + ); $this->_items['item1']->expects($this->any())->method("hasChildren")->will($this->returnValue(true)); @@ -285,11 +307,11 @@ public function testMultipleIterationsWorkProperly() $items[] = $item->getId(); } - $items2 = []; + $itemsTwo = []; foreach ($this->_model as $item) { - $items2[] = $item->getId(); + $itemsTwo[] = $item->getId(); } - $this->assertEquals($items, $items2); + $this->assertEquals($items, $itemsTwo); } /** @@ -307,10 +329,10 @@ public function testNestedLoop() 'item3' => ['item1', 'item2', 'item3'], ]; $actual = []; - foreach ($this->_model as $valLoop1) { - $keyLevel1 = $valLoop1->getId(); - foreach ($this->_model as $valLoop2) { - $actual[$keyLevel1][] = $valLoop2->getId(); + foreach ($this->_model as $valLoopOne) { + $keyLevelOne = $valLoopOne->getId(); + foreach ($this->_model as $valLoopTwo) { + $actual[$keyLevelOne][] = $valLoopTwo->getId(); } } $this->assertEquals($expected, $actual); @@ -318,7 +340,45 @@ public function testNestedLoop() public function testSerialize() { - $this->assertNotEmpty($this->_model->serialize()); - $this->_model->add($this->_items['item1']); + $serializerMock = $this->getMock(SerializerInterface::class); + $serializerMock->expects($this->once()) + ->method('serialize') + ->with([['arrayData']]) + ->willReturn('serializedString'); + $menu = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger, + 'serializer' => $serializerMock, + ] + ); + $itemMock = $this->getMock(\Magento\Backend\Model\Menu\Item::class, [], [], '', false); + $itemMock->expects($this->any())->method('getId')->will($this->returnValue('item1')); + $itemMock->expects($this->once()) + ->method('toArray') + ->willReturn(['arrayData']); + $menu->add($itemMock); + $this->assertEquals('serializedString', $menu->serialize()); + } + + public function testUnserialize() + { + $serializerMock = $this->getMock(SerializerInterface::class); + $serializerMock->expects($this->once()) + ->method('unserialize') + ->willReturn([['unserializedData']]); + $menuItemFactoryMock = $this->getMock(Factory::class, [], [], '', false); + $menuItemFactoryMock->expects($this->once()) + ->method('create') + ->with(['unserializedData']); + $menu = $this->objectManagerHelper->getObject( + \Magento\Backend\Model\Menu::class, + [ + 'logger' => $this->_logger, + 'serializer' => $serializerMock, + 'menuItemFactory' => $menuItemFactoryMock, + ] + ); + $menu->unserialize('serializedString'); } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/Session/AdminConfigTest.php b/app/code/Magento/Backend/Test/Unit/Model/Session/AdminConfigTest.php index fa5b86df391c3..53817fc21a955 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Session/AdminConfigTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Session/AdminConfigTest.php @@ -1,6 +1,6 @@ objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->customerRepositoryMock = $this->getMockForAbstractClass( \Magento\Customer\Api\CustomerRepositoryInterface::class, [], @@ -197,13 +198,6 @@ protected function setUp() ); $this->quoteFactoryMock = $this->getMock(\Magento\Quote\Model\QuoteFactory::class, ['create'], [], '', false); - $this->cartManagementMock = $this->getMock( - \Magento\Quote\Api\CartManagementInterface::class, - [], - [], - '', - false - ); $this->quote = $this->getMock( \Magento\Backend\Model\Session\Quote::class, @@ -226,10 +220,6 @@ protected function setUp() 'quoteFactory' => $this->quoteFactoryMock ] ); - - $this->prepareObjectManager([ - [\Magento\Quote\Api\CartManagementInterface::class, $this->cartManagementMock] - ]); } /** @@ -416,19 +406,4 @@ public function getQuoteDataProvider() 'customer ids same' => [66, 66, 'never'], ]; } - - /** - * @param array $map - * @deprecated - */ - private function prepareObjectManager($map) - { - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManagerInterface::class); - $objectManagerMock->expects($this->any())->method('getInstance')->willReturnSelf(); - $objectManagerMock->expects($this->any())->method('get')->will($this->returnValueMap($map)); - $reflectionClass = new \ReflectionClass(\Magento\Framework\App\ObjectManager::class); - $reflectionProperty = $reflectionClass->getProperty('_instance'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($objectManagerMock); - } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/Translate/Inline/ConfigTest.php b/app/code/Magento/Backend/Test/Unit/Model/Translate/Inline/ConfigTest.php index fbffdd4fdb16c..dbb03d249519f 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Translate/Inline/ConfigTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Translate/Inline/ConfigTest.php @@ -1,6 +1,6 @@ _menuMock = $this->getMock( - \Magento\Backend\Model\Menu::class, - [], - [$this->getMock(\Psr\Log\LoggerInterface::class)] - ); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->_menuMock = $this->getMock(\Magento\Backend\Model\Menu::class, [], [], '', false); $this->_menuConfigMock = $this->getMock(\Magento\Backend\Model\Menu\Config::class, [], [], '', false); $this->_menuConfigMock->expects($this->any())->method('getMenu')->will($this->returnValue($this->_menuMock)); @@ -141,25 +137,21 @@ protected function setUp() false, false ); - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->_encryptor = $this->getMock(\Magento\Framework\Encryption\Encryptor::class, null, [], '', false); - $this->_paramsResolverMock = $this->getMock( + $routeParamsResolver = $this->getMock(\Magento\Framework\Url\RouteParamsResolver::class, [], [], '', false); + $this->routeParamsResolverFactoryMock = $this->getMock( \Magento\Framework\Url\RouteParamsResolverFactory::class, [], [], '', false ); - $this->_paramsResolverMock->expects( - $this->any() - )->method( - 'create' - )->will( - $this->returnValue( - $this->getMock(\Magento\Framework\Url\RouteParamsResolver::class, [], [], '', false) - ) - ); - $this->_model = $helper->getObject( + $this->routeParamsResolverFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($routeParamsResolver); + /** @var HostChecker|\PHPUnit_Framework_MockObject_MockObject $hostCheckerMock */ + $hostCheckerMock = $this->getMock(HostChecker::class, [], [], '', false); + $this->_model = $objectManager->getObject( \Magento\Backend\Model\Url::class, [ 'scopeConfig' => $this->_scopeConfigMock, @@ -168,31 +160,10 @@ protected function setUp() 'menuConfig' => $this->_menuConfigMock, 'authSession' => $this->_authSessionMock, 'encryptor' => $this->_encryptor, - 'routeParamsResolverFactory' => $this->_paramsResolverMock + 'routeParamsResolverFactory' => $this->routeParamsResolverFactoryMock, + 'hostChecker' => $hostCheckerMock ] ); - $this->_paramsResolverMock->expects( - $this->any() - )->method( - 'create' - )->will( - $this->returnValue( - $this->getMock(\Magento\Framework\Url\RouteParamsResolver::class, [], [], '', false) - ) - ); - $this->_model = $helper->getObject( - \Magento\Backend\Model\Url::class, - [ - 'scopeConfig' => $this->_scopeConfigMock, - 'backendHelper' => $helperMock, - 'formKey' => $this->_formKey, - 'menuConfig' => $this->_menuConfigMock, - 'authSession' => $this->_authSessionMock, - 'encryptor' => $this->_encryptor, - 'routeParamsResolverFactory' => $this->_paramsResolverMock - ] - ); - $this->_requestMock = $this->getMock(\Magento\Framework\App\Request\Http::class, [], [], '', false); $this->_model->setRequest($this->_requestMock); } @@ -262,7 +233,7 @@ public function testGetAreaFrontName() [ 'backendHelper' => $helperMock, 'authSession' => $this->_authSessionMock, - 'routeParamsResolverFactory' => $this->_paramsResolverMock + 'routeParamsResolverFactory' => $this->routeParamsResolverFactoryMock ] ); $urlModel->getAreaFrontName(); diff --git a/app/code/Magento/Backend/Test/Unit/Model/View/Layout/ConditionPoolTest.php b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/ConditionPoolTest.php new file mode 100644 index 0000000000000..46cea7665cc3b --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/ConditionPoolTest.php @@ -0,0 +1,65 @@ +objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManager = new ObjectManager($this); + $this->conditionPool = $objectManager->getObject( + ConditionPool::class, + [ + 'conditions' => [ + 'condition-1' => 'Condition_1', + 'condition-2' => 'Condition_2', + ], + 'objectManager' => $this->objectManagerMock + ] + ); + } + + public function testGetCondition() + { + $this->objectManagerMock->expects($this->once()) + ->method('get') + ->with('Condition_1') + ->willReturn('Condition1Instance'); + $this->assertEquals( + 'Condition1Instance', + $this->conditionPool->getCondition('condition-1') + ); + } + + /** + * @expectedException \Magento\Framework\Exception\InputException + */ + public function testGetConditionUnknownCondition() + { + $this->conditionPool->getCondition('condition-3'); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/View/Layout/Filter/AclTest.php b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/Filter/AclTest.php index 820704c859769..9eb02e568439f 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/View/Layout/Filter/AclTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/Filter/AclTest.php @@ -1,28 +1,47 @@ authorizationMock = $this->getMockBuilder(\Magento\Framework\AuthorizationInterface::class) + $this->authorizationMock = $this->getMockBuilder(AuthorizationInterface::class) + ->getMock(); + $this->structureManager = $this->getMockBuilder(StructureManager::class) ->getMock(); - $this->model = new \Magento\Backend\Model\View\Layout\Filter\Acl($this->authorizationMock); + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Acl::class, + [ + 'authorization' => $this->authorizationMock + ] + ); + $objectManager->setBackwardCompatibleProperty($this->model, 'structureManager', $this->structureManager); } public function testFilterAclElements() @@ -86,28 +105,8 @@ public function testFilterAclElements() ] ); - $structureMock->expects($this->exactly(3)) - ->method('getChildren') - ->willReturnMap( - [ - ['element_2', ['element_2_child' => []]], - ['element_2_child', []], - ['element_3', []], - ] - ); - - $scheduledStructureMock->expects($this->exactly(3)) - ->method('unsetElement') - ->willReturnMap( - [ - ['element_2', null], - ['element_2_child', null], - ['element_3', null], - ] - ); - - $structureMock->expects($this->exactly(2)) - ->method('unsetElement') + $this->structureManager->expects($this->exactly(2)) + ->method('removeElement') ->willReturnMap( [ ['element_2', true, true], diff --git a/app/code/Magento/Backend/Test/Unit/Model/View/Layout/Filter/ConditionTest.php b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/Filter/ConditionTest.php new file mode 100644 index 0000000000000..dacfbac3c4aa7 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/Filter/ConditionTest.php @@ -0,0 +1,139 @@ +structureManagerMock = $this->getMockBuilder(StructureManager::class) + ->getMock(); + $this->structureMock = $this->getMockBuilder(Structure::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scheduledStructureMock = $this->getMockBuilder(ScheduledStructure::class) + ->getMock(); + $this->conditionPoolMock = $this->getMockBuilder(ConditionPool::class) + ->disableOriginalConstructor() + ->getMock(); + $this->conditionMock = $this->getMockBuilder(ConditionInterface::class) + ->getMock(); + $objectManager = new ObjectManager($this); + + $this->filter = $objectManager->getObject( + Condition::class, + [ + 'structureManager' => $this->structureManagerMock, + 'conditionPool' => $this->conditionPoolMock + ] + ); + } + + private function getStructureData() + { + return [ + 'element_0' => [ + 0 => '', + 1 => [ + 'attributes' => [ + 'name' => 'element_0', + ], + ], + ], + 'element_1' => [ + 0 => '', + 1 => [ + 'attributes' => [ + 'name' => 'element_1', + 'condition' => 'TestCondition1', + ], + ], + ], + 'element_2' => [ + 0 => '', + 1 => [ + 'attributes' => [ + 'name' => 'element_2', + 'condition' => 'TestCondition2', + ], + ], + ], + 'element_3' => [ + 0 => '', + 1 => [ + 'attributes' => [ + 'name' => 'element_3', + 'acl' => 'acl_non_authorised', + ], + ], + ], + ]; + } + + public function testFilterElement() + { + $this->scheduledStructureMock->expects($this->once()) + ->method('getElements') + ->willReturn($this->getStructureData()); + $this->conditionPoolMock->expects($this->exactly(2)) + ->method('getCondition') + ->willReturnMap( + [ + ['TestCondition1', $this->conditionMock], + ['TestCondition2', $this->conditionMock] + ] + ); + $this->conditionMock->expects($this->at(0)) + ->method('validate') + ->willReturn(false); + $this->conditionMock->expects($this->at(1)) + ->method('validate') + ->willReturn(true); + $this->structureManagerMock->expects($this->once()) + ->method('removeElement') + ->with($this->scheduledStructureMock, $this->structureMock, 'element_1') + ->willReturn(true); + $this->assertTrue($this->filter->filterElement($this->scheduledStructureMock, $this->structureMock)); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/View/Layout/FilterTest.php b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/FilterTest.php new file mode 100644 index 0000000000000..0a51c9c1c56f3 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/FilterTest.php @@ -0,0 +1,65 @@ +someFilterMock = $this->getMockBuilder(FilterInterface::class) + ->getMock(); + $this->structureMock = $this->getMockBuilder(Structure::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scheduledStructureMock = $this->getMockBuilder(ScheduledStructure::class) + ->getMock(); + $objectManager = new ObjectManager($this); + $this->filter = $objectManager->getObject( + Filter::class, + [ + 'filters' => + [ + 'filter1' => $this->someFilterMock + ] + ] + ); + } + + public function testFilterElement() + { + $this->someFilterMock->expects($this->once()) + ->method('filterElement') + ->with($this->scheduledStructureMock, $this->structureMock); + $this->assertTrue($this->filter->filterElement($this->scheduledStructureMock, $this->structureMock)); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/View/Layout/StructureManagerTest.php b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/StructureManagerTest.php new file mode 100644 index 0000000000000..a5a9055c0d03f --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/View/Layout/StructureManagerTest.php @@ -0,0 +1,87 @@ +structureMock = $this->getMockBuilder(Structure::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scheduledStructureMock = $this->getMockBuilder(ScheduledStructure::class) + ->disableOriginalConstructor() + ->getMock(); + $this->structureManager = $objectManager->getObject(StructureManager::class); + } + + public function testRemoveElement() + { + $this->structureMock->expects($this->exactly(3)) + ->method('getChildren') + ->willReturnMap( + [ + [ + 'element-0', [ + 'element-1' => [], + 'element-2' => [] + ] + ], + [ + 'element-1', [] + ], + [ + 'element-2', [] + ] + ] + ); + $this->scheduledStructureMock->expects($this->exactly(3)) + ->method('unsetElement') + ->willReturnMap( + [ + ['element-0', true], + ['element-1', true], + ['element-2', true] + ] + ); + $this->structureMock->expects($this->once()) + ->method('unsetElement') + ->with('element-0'); + $this->assertTrue( + $this->structureManager->removeElement( + $this->scheduledStructureMock, + $this->structureMock, + 'element-0', + false + ) + ); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/View/Result/PageTest.php b/app/code/Magento/Backend/Test/Unit/Model/View/Result/PageTest.php index f88692029c159..3f1a6834f25a9 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/View/Result/PageTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/View/Result/PageTest.php @@ -1,6 +1,6 @@ [ + [], + [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + [ + 'parent_id' => null, + 'module_name' => 'Magento_Backend', + 'sort_index' => null, + 'depends_on_config' => 'system/config/isEnabled', + 'id' => 'item', + 'resource' => 'Magento_Config::config', + 'path' => '', + 'action' => '/system/config', + 'depends_on_module' => 'Magento_Backend', + 'tooltip' => 'Item tooltip', + 'title' => 'Item Title', + 'sub_menu' => null, + 'target' => null + ], + ], + 'data without submenu to constructor' => [ + [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => null, + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => null, + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => '', + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => null, + 'sub_menu' => ['submenuArray'], + 'target' => null + ], + ], + 'data with submenu to constructor' => [ + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => null, + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => null, + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => null, + 'resource' => null, + 'path' => '', + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => null, + 'sub_menu' => ['submenuArray'], + 'target' => null + ], + ] +]; diff --git a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php new file mode 100644 index 0000000000000..bc336aad5783d --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php @@ -0,0 +1,121 @@ + [ + [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + 'sub_menu' => null, + ], + [ + 'parent_id' => null, + 'module_name' => 'Magento_Backend', + 'sort_index' => null, + 'depends_on_config' => 'system/config/isEnabled', + 'id' => 'item', + 'resource' => 'Magento_Config::config', + 'path' => '', + 'action' => '/system/config', + 'depends_on_module' => 'Magento_Backend', + 'tooltip' => 'Item tooltip', + 'title' => 'Item Title', + 'sub_menu' => null, + 'target' => null + ] + ], + 'with submenu' => [ + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => null, + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => null, + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => '5', + 'resource' => null, + 'path' => null, + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + 'target' => null + ] + ], + 'small set of data' => [ + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + ], + [ + 'parent_id' => '1', + 'module_name' => 'Magento_Module1', + 'sort_index' => '50', + 'depends_on_config' => null, + 'id' => null, + 'resource' => null, + 'path' => '', + 'action' => null, + 'depends_on_module' => null, + 'tooltip' => '', + 'title' => null, + 'sub_menu' => [ + 'id' => 'item', + 'title' => 'Item Title', + 'action' => '/system/config', + 'resource' => 'Magento_Config::config', + 'depends_on_module' => 'Magento_Backend', + 'depends_on_config' => 'system/config/isEnabled', + 'tooltip' => 'Item tooltip', + ], + 'target' => null + ] + ] +]; diff --git a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_merged.php b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_merged.php index ad86dea88ef08..c5213cdab9d87 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_merged.php +++ b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_merged.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_merged.xml b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_merged.xml index 55ed5a90d8025..e7ac5aec547d2 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_merged.xml +++ b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_merged.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/Test/Unit/Setup/ConfigOptionsListTest.php b/app/code/Magento/Backend/Test/Unit/Setup/ConfigOptionsListTest.php index d7a8de1012b82..dd90832aa38b4 100644 --- a/app/code/Magento/Backend/Test/Unit/Setup/ConfigOptionsListTest.php +++ b/app/code/Magento/Backend/Test/Unit/Setup/ConfigOptionsListTest.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index fcf2cc02e9fb9..9488d35adcc87 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -1,7 +1,7 @@ @@ -141,4 +141,12 @@ + + + + Magento\Config\Model\Config\Structure\ElementVisibilityInterface::HIDDEN + Magento\Config\Model\Config\Structure\ElementVisibilityInterface::DISABLED + + + diff --git a/app/code/Magento/Backend/etc/adminhtml/menu.xml b/app/code/Magento/Backend/etc/adminhtml/menu.xml index 1a754291cb47c..d7d57b7953c77 100644 --- a/app/code/Magento/Backend/etc/adminhtml/menu.xml +++ b/app/code/Magento/Backend/etc/adminhtml/menu.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/etc/adminhtml/routes.xml b/app/code/Magento/Backend/etc/adminhtml/routes.xml index 232ac5222daea..2f3857c91c5c0 100644 --- a/app/code/Magento/Backend/etc/adminhtml/routes.xml +++ b/app/code/Magento/Backend/etc/adminhtml/routes.xml @@ -1,7 +1,7 @@ @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 0eb01d6a252ea..d33abf75acd56 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -1,7 +1,7 @@ @@ -16,7 +16,11 @@ -
    + +
    advanced Magento_Backend::advanced diff --git a/app/code/Magento/Backend/etc/config.xml b/app/code/Magento/Backend/etc/config.xml index 459dd377e36ba..1d347c7471725 100644 --- a/app/code/Magento/Backend/etc/config.xml +++ b/app/code/Magento/Backend/etc/config.xml @@ -1,7 +1,7 @@ @@ -11,6 +11,9 @@ + + 1 + diff --git a/app/code/Magento/Backend/etc/crontab.xml b/app/code/Magento/Backend/etc/crontab.xml index 0c7e18e977549..4f6450a7226ae 100644 --- a/app/code/Magento/Backend/etc/crontab.xml +++ b/app/code/Magento/Backend/etc/crontab.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/etc/di.xml b/app/code/Magento/Backend/etc/di.xml index 8b52d08da48fb..8894f2293fa35 100644 --- a/app/code/Magento/Backend/etc/di.xml +++ b/app/code/Magento/Backend/etc/di.xml @@ -1,7 +1,7 @@ @@ -14,6 +14,7 @@ + @@ -180,37 +181,36 @@ - + + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + + - - - design/theme/theme_id - theme - Magento\Theme\Model\Design\Backend\Theme - true - - - design/theme/ua_regexp - desing_rule - Magento\Theme\Model\Design\Backend\Exceptions - - - design/pagination/pagination_frame - other_settings/pagination - - - design/pagination/pagination_frame_skip - other_settings/pagination - - - design/pagination/anchor_text_for_previous - other_settings/pagination - - - design/pagination/anchor_text_for_next - other_settings/pagination - + + Magento\Backend\Model\View\Layout\Filter\Acl + Magento\Backend\Model\View\Layout\Filter\Condition + + + Magento\Backend\Block\MenuItemChecker + Magento\Backend\Block\AnchorRenderer + + diff --git a/app/code/Magento/Backend/etc/menu.xsd b/app/code/Magento/Backend/etc/menu.xsd index 05df67a5e2bbd..f7a22103e2e90 100644 --- a/app/code/Magento/Backend/etc/menu.xsd +++ b/app/code/Magento/Backend/etc/menu.xsd @@ -1,7 +1,7 @@ @@ -31,6 +31,7 @@ + @@ -51,6 +52,7 @@ + diff --git a/app/code/Magento/Backend/etc/module.xml b/app/code/Magento/Backend/etc/module.xml index db6b19407c73b..b7f188e78d34e 100644 --- a/app/code/Magento/Backend/etc/module.xml +++ b/app/code/Magento/Backend/etc/module.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/etc/webapi.xml b/app/code/Magento/Backend/etc/webapi.xml index d01bbafa8e676..14d4dccc78cbe 100644 --- a/app/code/Magento/Backend/etc/webapi.xml +++ b/app/code/Magento/Backend/etc/webapi.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/i18n/en_US.csv b/app/code/Magento/Backend/i18n/en_US.csv index 252269ba97ffb..9144df6c8f7d9 100644 --- a/app/code/Magento/Backend/i18n/en_US.csv +++ b/app/code/Magento/Backend/i18n/en_US.csv @@ -250,7 +250,7 @@ Minute,Minute "JavaScript may be disabled in your browser.","JavaScript may be disabled in your browser." "To use this website you must first enable JavaScript in your browser.","To use this website you must first enable JavaScript in your browser." "This is only a demo store. You can browse and place orders, but nothing will be processed.","This is only a demo store. You can browse and place orders, but nothing will be processed." -"Report Bugs","Report Bugs" +"Report a Bug","Report a Bug" "Store View:","Store View:" "Stores Configuration","Stores Configuration" "Please confirm scope switching. All data that hasn\'t been saved will be lost.","Please confirm scope switching. All data that hasn\'t been saved will be lost." diff --git a/app/code/Magento/Backend/registration.php b/app/code/Magento/Backend/registration.php index 5e9d9fe4dabe7..fac71f545151f 100644 --- a/app/code/Magento/Backend/registration.php +++ b/app/code/Magento/Backend/registration.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_auth_login.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_auth_login.xml index d8462aeedfa1a..bb7370b3317a4 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_auth_login.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_auth_login.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml index 3e61fec077c6e..e4f6fbea1cd29 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml @@ -1,7 +1,7 @@ @@ -23,10 +23,12 @@ Enable adminhtml/*/massEnable + Magento\Backend\Block\Cache\Grid\Massaction\ProductionModeVisibilityChecker Disable adminhtml/*/massDisable + Magento\Backend\Block\Cache\Grid\Massaction\ProductionModeVisibilityChecker Refresh @@ -48,6 +50,7 @@ 180 left 0 + true @@ -57,6 +60,7 @@ text left 0 + true diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_index.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_index.xml index 8edb476a2b555..77ba8f6f39824 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_index.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_customersmost.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_customersmost.xml index 39b3db77d505e..31b3ff2c44bc1 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_customersmost.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_customersmost.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_customersnewest.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_customersnewest.xml index ce83aa64faafb..ce4e20140eb54 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_customersnewest.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_customersnewest.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml index 8b36caac55b82..b353b81aa1b7c 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_productsviewed.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_productsviewed.xml index d55194f4dbb43..4621d83dc6753 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_productsviewed.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_dashboard_productsviewed.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_denied.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_denied.xml index e8754242dfd2f..fac9407cc3298 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_denied.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_denied.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_noroute.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_noroute.xml index 4872a39a16e45..598bdf490e8a5 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_noroute.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_noroute.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_account_index.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_account_index.xml index 581f6d2ee5972..dfc27d5e6b5e0 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_account_index.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_account_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_edit.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_edit.xml index 4f5d3a778a120..91469c51e06c9 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_edit.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_edit.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid.xml index 4a0c8a711f5ea..bda7f3883adce 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid_block.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid_block.xml index 3d2c4dff37228..a39aee9813286 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid_block.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid_block.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_index.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_index.xml index 8ae928a3cadcf..7412c983cff8b 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_index.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_grid_block.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_grid_block.xml index 320ce474bc392..0521a87831663 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_grid_block.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_grid_block.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_index.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_index.xml index 64d7968bd1772..2abd830f5fa37 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_index.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/default.xml b/app/code/Magento/Backend/view/adminhtml/layout/default.xml index 9db902fb13294..ea98fd70740aa 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/default.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/default.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/editor.xml b/app/code/Magento/Backend/view/adminhtml/layout/editor.xml index 9109e54ac357b..2c34667c0502e 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/editor.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/editor.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/empty.xml b/app/code/Magento/Backend/view/adminhtml/layout/empty.xml index 7509438df2553..e01b48cc71b11 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/empty.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/empty.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/formkey.xml b/app/code/Magento/Backend/view/adminhtml/layout/formkey.xml index 50b1784b33210..6ba9743703ed4 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/formkey.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/formkey.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/overlay_popup.xml b/app/code/Magento/Backend/view/adminhtml/layout/overlay_popup.xml index 67304406b201b..8a48fcca66c0e 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/overlay_popup.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/overlay_popup.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/layout/popup.xml b/app/code/Magento/Backend/view/adminhtml/layout/popup.xml index 825094937cd9d..dd65940af81e5 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/popup.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/popup.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/requirejs-config.js b/app/code/Magento/Backend/view/adminhtml/requirejs-config.js index 9c1350ea268cb..338490f3f4413 100644 --- a/app/code/Magento/Backend/view/adminhtml/requirejs-config.js +++ b/app/code/Magento/Backend/view/adminhtml/requirejs-config.js @@ -1,8 +1,8 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -/*eslint no-unused-vars: 0*/ + var config = { map: { '*': { diff --git a/app/code/Magento/Backend/view/adminhtml/templates/admin/access_denied.phtml b/app/code/Magento/Backend/view/adminhtml/templates/admin/access_denied.phtml index 7ff08dcc32422..32910c9978d2d 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/admin/access_denied.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/admin/access_denied.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/templates/admin/login.phtml b/app/code/Magento/Backend/view/adminhtml/templates/admin/login.phtml index 0968047053bc5..1716af3ec14c4 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/admin/login.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/admin/login.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/templates/admin/overlay_popup.phtml b/app/code/Magento/Backend/view/adminhtml/templates/admin/overlay_popup.phtml index ab29376b3b573..e95b063599daa 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/admin/overlay_popup.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/admin/overlay_popup.phtml @@ -1,6 +1,6 @@
    -
    + getChildHtml(); ?> + + + value="escapeHtmlAttr($block->getFormKey()); ?>"/>
    diff --git a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml index e93b143e039c8..aeac3fd33248c 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/templates/page/notices.phtml b/app/code/Magento/Backend/view/adminhtml/templates/page/notices.phtml index 987afc07df489..b4e7c2d7f50a8 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/page/notices.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/page/notices.phtml @@ -1,6 +1,6 @@ getBugreportUrl()): ?> - + diff --git a/app/code/Magento/Backend/view/adminhtml/templates/pageactions.phtml b/app/code/Magento/Backend/view/adminhtml/templates/pageactions.phtml index c712eeef6db74..1b0a1d9a662ef 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/pageactions.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/pageactions.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/templates/system/design/index.phtml b/app/code/Magento/Backend/view/adminhtml/templates/system/design/index.phtml index 3b5748da54823..2cdb9f451a86f 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/system/design/index.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/system/design/index.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/accordion.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/accordion.phtml index 52e4d895c1dc1..060ee67e9a7b2 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/accordion.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/accordion.phtml @@ -1,6 +1,6 @@ getExportButtonHtml() ?> -
    \ No newline at end of file +
    diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml index 62ff8d2752362..838e22f16493a 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/ui_component/design_config_listing.xml b/app/code/Magento/Backend/view/adminhtml/ui_component/design_config_listing.xml index c72e5f3c9079e..c299f217c8df7 100644 --- a/app/code/Magento/Backend/view/adminhtml/ui_component/design_config_listing.xml +++ b/app/code/Magento/Backend/view/adminhtml/ui_component/design_config_listing.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/bootstrap/editor.js b/app/code/Magento/Backend/view/adminhtml/web/js/bootstrap/editor.js index 08ff14f84f660..9e57f2fd5bd4b 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/bootstrap/editor.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/bootstrap/editor.js @@ -1,9 +1,10 @@ /** * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + require([ - "Magento_Variable/variables", - "mage/adminhtml/browser" -]); \ No newline at end of file + 'Magento_Variable/variables', + 'mage/adminhtml/browser' +]); diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js index 18f44da26109a..fe7c8dd7cb800 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js @@ -1,6 +1,6 @@ /** * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ /*global byteConvert*/ diff --git a/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/cells/action-delete.html b/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/cells/action-delete.html index 44c460825b45d..e60fc8c2c238e 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/cells/action-delete.html +++ b/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/cells/action-delete.html @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html b/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html index 3a38ced51f7ed..8714e443faf28 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html +++ b/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backend/view/adminhtml/web/template/form/element/helper/fallback-reset-link.html b/app/code/Magento/Backend/view/adminhtml/web/template/form/element/helper/fallback-reset-link.html index 25b584f89f9fa..f788dbb6fbe20 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/template/form/element/helper/fallback-reset-link.html +++ b/app/code/Magento/Backend/view/adminhtml/web/template/form/element/helper/fallback-reset-link.html @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backup/Block/Adminhtml/Backup.php b/app/code/Magento/Backup/Block/Adminhtml/Backup.php index 354dca87a79a9..0aa4f8833e9ea 100644 --- a/app/code/Magento/Backup/Block/Adminhtml/Backup.php +++ b/app/code/Magento/Backup/Block/Adminhtml/Backup.php @@ -1,6 +1,6 @@ _view->loadLayout(); - $this->_view->renderLayout(); + return $this->resultFactory->create(ResultFactory::TYPE_PAGE); } } diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index/Index.php b/app/code/Magento/Backup/Controller/Adminhtml/Index/Index.php index d845a28569f63..f784e507e96b1 100644 --- a/app/code/Magento/Backup/Controller/Adminhtml/Index/Index.php +++ b/app/code/Magento/Backup/Controller/Adminhtml/Index/Index.php @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backup/etc/adminhtml/menu.xml b/app/code/Magento/Backup/etc/adminhtml/menu.xml index 812b1cb9d0df8..32c2936697fac 100644 --- a/app/code/Magento/Backup/etc/adminhtml/menu.xml +++ b/app/code/Magento/Backup/etc/adminhtml/menu.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backup/etc/adminhtml/routes.xml b/app/code/Magento/Backup/etc/adminhtml/routes.xml index 3e0e606439eed..232c0ca0f9d6a 100644 --- a/app/code/Magento/Backup/etc/adminhtml/routes.xml +++ b/app/code/Magento/Backup/etc/adminhtml/routes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backup/etc/adminhtml/system.xml b/app/code/Magento/Backup/etc/adminhtml/system.xml index 69e15030ad6a7..325395826df15 100644 --- a/app/code/Magento/Backup/etc/adminhtml/system.xml +++ b/app/code/Magento/Backup/etc/adminhtml/system.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backup/etc/crontab.xml b/app/code/Magento/Backup/etc/crontab.xml index a0a1fdbdf2a15..150751eb94a1f 100644 --- a/app/code/Magento/Backup/etc/crontab.xml +++ b/app/code/Magento/Backup/etc/crontab.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backup/etc/di.xml b/app/code/Magento/Backup/etc/di.xml index f9371f4a249bf..fc9c5bb2ff025 100644 --- a/app/code/Magento/Backup/etc/di.xml +++ b/app/code/Magento/Backup/etc/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backup/etc/module.xml b/app/code/Magento/Backup/etc/module.xml index 9edced27b2247..9f4fe8da7ac1d 100644 --- a/app/code/Magento/Backup/etc/module.xml +++ b/app/code/Magento/Backup/etc/module.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backup/registration.php b/app/code/Magento/Backup/registration.php index c158dddf70a65..d429ad8208552 100644 --- a/app/code/Magento/Backup/registration.php +++ b/app/code/Magento/Backup/registration.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_grid.xml b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_grid.xml index 1f6e1cbec7f10..03b4aa4a7f4ad 100644 --- a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_grid.xml +++ b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_grid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_index.xml b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_index.xml index 42c3d766b6997..242534d006e37 100644 --- a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_index.xml +++ b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Backup/view/adminhtml/templates/backup/dialogs.phtml b/app/code/Magento/Backup/view/adminhtml/templates/backup/dialogs.phtml index c3ac897edd8e5..597651f8329e3 100644 --- a/app/code/Magento/Backup/view/adminhtml/templates/backup/dialogs.phtml +++ b/app/code/Magento/Backup/view/adminhtml/templates/backup/dialogs.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Backup/view/adminhtml/templates/backup/list.phtml b/app/code/Magento/Backup/view/adminhtml/templates/backup/list.phtml index 0fff82609ed11..ace0931b60428 100644 --- a/app/code/Magento/Backup/view/adminhtml/templates/backup/list.phtml +++ b/app/code/Magento/Backup/view/adminhtml/templates/backup/list.phtml @@ -1,6 +1,6 @@ orderPlace = $orderPlace; + $this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class); } /** @@ -58,6 +70,7 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ return $resultRedirect->setPath('checkout/onepage/success', ['_secure' => true]); } catch (\Exception $e) { + $this->logger->critical($e); $this->messageManager->addExceptionMessage($e, $e->getMessage()); } diff --git a/app/code/Magento/Braintree/Controller/Paypal/Review.php b/app/code/Magento/Braintree/Controller/Paypal/Review.php index 1b6c3a3ab5a91..a6d2234f26aba 100644 --- a/app/code/Magento/Braintree/Controller/Paypal/Review.php +++ b/app/code/Magento/Braintree/Controller/Paypal/Review.php @@ -1,6 +1,6 @@ serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(Json::class); + } + /** * Get list of available dynamic descriptors keys * @var array @@ -45,7 +72,7 @@ class Config extends \Magento\Payment\Gateway\Config\Config */ public function getCountrySpecificCardTypeConfig() { - $countriesCardTypes = unserialize($this->getValue(self::KEY_COUNTRY_CREDIT_CARD)); + $countriesCardTypes = $this->serializer->unserialize($this->getValue(self::KEY_COUNTRY_CREDIT_CARD)); return is_array($countriesCardTypes) ? $countriesCardTypes : []; } diff --git a/app/code/Magento/Braintree/Gateway/Config/PayPal/Config.php b/app/code/Magento/Braintree/Gateway/Config/PayPal/Config.php index f94c6abfd773b..3f34afd0717db 100644 --- a/app/code/Magento/Braintree/Gateway/Config/PayPal/Config.php +++ b/app/code/Magento/Braintree/Gateway/Config/PayPal/Config.php @@ -1,6 +1,6 @@ paymentTokenFactory = $paymentTokenFactory; $this->paymentExtensionFactory = $paymentExtensionFactory; $this->config = $config; $this->subjectReader = $subjectReader; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** @@ -133,7 +143,7 @@ private function getExpirationDate(Transaction $transaction) */ private function convertDetailsToJSON($details) { - $json = \Zend_Json::encode($details); + $json = $this->serializer->serialize($details); return $json ? $json : '{}'; } diff --git a/app/code/Magento/Braintree/Gateway/Response/VoidHandler.php b/app/code/Magento/Braintree/Gateway/Response/VoidHandler.php index 5fc173af704a8..c3534d4a906e4 100644 --- a/app/code/Magento/Braintree/Gateway/Response/VoidHandler.php +++ b/app/code/Magento/Braintree/Gateway/Response/VoidHandler.php @@ -1,6 +1,6 @@ collectionFactory = $factory; $this->countryConfig = $countryConfig; } diff --git a/app/code/Magento/Braintree/Model/Adapter/BraintreeAdapter.php b/app/code/Magento/Braintree/Model/Adapter/BraintreeAdapter.php index 12e7cd8f532cc..c53d9e3f5ba8c 100644 --- a/app/code/Magento/Braintree/Model/Adapter/BraintreeAdapter.php +++ b/app/code/Magento/Braintree/Model/Adapter/BraintreeAdapter.php @@ -1,6 +1,6 @@ mathRandom = $mathRandom; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(Json::class); parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data); } @@ -68,7 +78,7 @@ public function beforeSave() $result[$country] = $data['cc_types']; } } - $this->setValue(serialize($result)); + $this->setValue($this->serializer->serialize($result)); return $this; } @@ -79,7 +89,7 @@ public function beforeSave() */ public function afterLoad() { - $value = unserialize($this->getValue()); + $value = $this->serializer->unserialize($this->getValue()); if (is_array($value)) { $value = $this->encodeArrayFieldValue($value); $this->setValue($value); diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/AbstractHelper.php b/app/code/Magento/Braintree/Model/Paypal/Helper/AbstractHelper.php index ad72fcf8a424b..0a5583d465f68 100644 --- a/app/code/Magento/Braintree/Model/Paypal/Helper/AbstractHelper.php +++ b/app/code/Magento/Braintree/Model/Paypal/Helper/AbstractHelper.php @@ -1,6 +1,6 @@ componentFactory = $componentFactory; + $this->urlBuilder = $urlBuilder; + $this->config = $config; + } + + /** + * @inheritdoc + */ + public function getComponentForToken(PaymentTokenInterface $paymentToken) + { + $data = json_decode($paymentToken->getTokenDetails() ?: '{}', true); + $data['icon'] = $this->config->getPayPalIcon(); + $component = $this->componentFactory->create( + [ + 'config' => [ + 'code' => PayPalConfigProvider::PAYPAL_VAULT_CODE, + 'nonceUrl' => $this->getNonceRetrieveUrl(), + TokenUiComponentProviderInterface::COMPONENT_DETAILS => $data, + TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH => $paymentToken->getPublicHash(), + 'template' => 'Magento_Braintree::form/paypal/vault.phtml' + ], + 'name' => Template::class + ] + ); + + return $component; + } + + /** + * Get url to retrieve payment method nonce + * @return string + */ + private function getNonceRetrieveUrl() + { + return $this->urlBuilder->getUrl(ConfigProvider::CODE . '/payment/getnonce', ['_secure' => true]); + } +} diff --git a/app/code/Magento/Braintree/Model/Ui/Adminhtml/TokenUiComponentProvider.php b/app/code/Magento/Braintree/Model/Ui/Adminhtml/TokenUiComponentProvider.php index 6cfc96ea23d0d..b2c6b53e2538a 100644 --- a/app/code/Magento/Braintree/Model/Ui/Adminhtml/TokenUiComponentProvider.php +++ b/app/code/Magento/Braintree/Model/Ui/Adminhtml/TokenUiComponentProvider.php @@ -1,6 +1,6 @@ componentFactory->create( [ 'config' => [ + 'code' => ConfigProvider::CC_VAULT_CODE, 'nonceUrl' => $this->getNonceRetrieveUrl(), TokenUiComponentProviderInterface::COMPONENT_DETAILS => $data, TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH => $paymentToken->getPublicHash(), diff --git a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php index 76788c3c14510..215f51627f194 100644 --- a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php +++ b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php @@ -1,6 +1,6 @@ fieldDataConverterFactory = $fieldDataConverterFactory; + $this->queryModifierFactory = $queryModifierFactory; + } + + /** + * Upgrades data for Braintree module + * + * @param ModuleDataSetupInterface $setup + * @param ModuleContextInterface $context + * @return void + */ + public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + if (version_compare($context->getVersion(), '2.0.1', '<')) { + $this->convertSerializedDataToJson($setup); + } + } + + /** + * Upgrade data to version 2.0.1, converts row data in the core_config_data table that uses the path + * payment/braintree/countrycreditcard from serialized to JSON + * + * @param ModuleDataSetupInterface $setup + * @return void + */ + private function convertSerializedDataToJson(ModuleDataSetupInterface $setup) + { + $fieldDataConverter = $this->fieldDataConverterFactory->create( + \Magento\Framework\DB\DataConverter\SerializedToJson::class + ); + + $queryModifier = $this->queryModifierFactory->create( + 'in', + [ + 'values' => [ + 'path' => ['payment/braintree/countrycreditcard'] + ] + ] + ); + + $fieldDataConverter->convert( + $setup->getConnection(), + $setup->getTable('core_config_data'), + 'config_id', + 'value', + $queryModifier + ); + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Block/FormTest.php b/app/code/Magento/Braintree/Test/Unit/Block/FormTest.php index e6c7ce59d0e29..4d2b2cba3faaf 100644 --- a/app/code/Magento/Braintree/Test/Unit/Block/FormTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Block/FormTest.php @@ -1,6 +1,6 @@ method('getMessageManager') ->willReturn($this->messageManagerMock); + $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); $this->placeOrder = new PlaceOrder( $contextMock, $this->configMock, $this->checkoutSessionMock, - $this->orderPlaceMock + $this->orderPlaceMock, + $this->loggerMock ); } diff --git a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php index a5cdaf3e35b70..88f0c4c11c829 100644 --- a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php @@ -1,6 +1,6 @@ scopeConfigMock = $this->getMock(ScopeConfigInterface::class); + $this->serializerMock = $this->getMock(Json::class); - $this->model = new Config($this->scopeConfigMock, self::METHOD_CODE); + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Config::class, + [ + 'scopeConfig' => $this->scopeConfigMock, + 'methodCode' => self::METHOD_CODE, + 'serializer' => $this->serializerMock + ] + ); } /** - * @param string $value + * @param string $encodedValue + * @param string|array $value * @param array $expected * @dataProvider getCountrySpecificCardTypeConfigDataProvider */ - public function testGetCountrySpecificCardTypeConfig($value, $expected) + public function testGetCountrySpecificCardTypeConfig($encodedValue, $value, array $expected) { $this->scopeConfigMock->expects(static::once()) ->method('getValue') ->with($this->getPath(Config::KEY_COUNTRY_CREDIT_CARD), ScopeInterface::SCOPE_STORE, null) + ->willReturn($encodedValue); + + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->with($encodedValue) ->willReturn($value); static::assertEquals( @@ -58,11 +80,13 @@ public function testGetCountrySpecificCardTypeConfig($value, $expected) public function getCountrySpecificCardTypeConfigDataProvider() { return [ - [ - serialize(['GB' => ['VI', 'AE'], 'US' => ['DI', 'JCB']]), + 'valid data' => [ + '{"GB":["VI","AE"],"US":["DI","JCB"]}', + ['GB' => ['VI', 'AE'], 'US' => ['DI', 'JCB']], ['GB' => ['VI', 'AE'], 'US' => ['DI', 'JCB']] ], - [ + 'non-array value' => [ + '""', '', [] ] @@ -146,12 +170,20 @@ public function getCcTypesMapperDataProvider() /** * @covers \Magento\Braintree\Gateway\Config\Config::getCountryAvailableCardTypes * @dataProvider getCountrySpecificCardTypeConfigDataProvider + * @param string $encodedData + * @param string|array $data + * @param array $countryData */ - public function testCountryAvailableCardTypes($data, $countryData) + public function testCountryAvailableCardTypes($encodedData, $data, array $countryData) { $this->scopeConfigMock->expects(static::any()) ->method('getValue') ->with($this->getPath(Config::KEY_COUNTRY_CREDIT_CARD), ScopeInterface::SCOPE_STORE, null) + ->willReturn($encodedData); + + $this->serializerMock->expects($this->any()) + ->method('unserialize') + ->with($encodedData) ->willReturn($data); foreach ($countryData as $countryId => $types) { diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Helper/SubjectReaderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Helper/SubjectReaderTest.php index 02192c819daf5..f4056c8abb3f3 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Helper/SubjectReaderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Helper/SubjectReaderTest.php @@ -1,6 +1,6 @@ method('getCctypesMapper') ->willReturn($mapperArray); + $this->serializer = $this->getMock( + \Magento\Framework\Serialize\Serializer\Json::class, + [], + [], + '', + false + ); + $this->paymentHandler = new VaultDetailsHandler( $this->paymentTokenFactory, $this->paymentExtensionFactory, $this->config, - $this->subjectReader + $this->subjectReader, + $this->serializer ); } diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VoidHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VoidHandlerTest.php index f0ec710e13827..51971e077f91a 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VoidHandlerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VoidHandlerTest.php @@ -1,6 +1,6 @@ resourceMock = $this->getMockForAbstractClass(AbstractResource::class); $this->mathRandomMock = $this->getMockBuilder(Random::class) ->disableOriginalConstructor() ->getMock(); + $this->serializerMock = $this->getMock(\Magento\Framework\Serialize\Serializer\Json::class); $this->objectManager = new ObjectManager($this); $this->model = $this->objectManager->getObject( @@ -50,18 +56,28 @@ protected function setUp() [ 'mathRandom' => $this->mathRandomMock, 'resource' => $this->resourceMock, + 'serializer' => $this->serializerMock ] ); } /** * @dataProvider beforeSaveDataProvider + * @param array $value + * @param array $expectedValue + * @param string $encodedValue */ - public function testBeforeSave($value, $expectedValue) + public function testBeforeSave(array $value, array $expectedValue, $encodedValue) { $this->model->setValue($value); + + $this->serializerMock->expects($this->once()) + ->method('serialize') + ->with($expectedValue) + ->willReturn($encodedValue); + $this->model->beforeSave(); - $this->assertEquals($expectedValue, $this->model->getValue()); + $this->assertEquals($encodedValue, $this->model->getValue()); } /** @@ -73,11 +89,13 @@ public function beforeSaveDataProvider() return [ 'empty_value' => [ 'value' => [], - 'expected' => serialize([]), + 'expected' => [], + 'encoded' => '[]' ], 'not_array' => [ 'value' => ['US'], - 'expected' => serialize([]), + 'expected' => [], + 'encoded' => '[]' ], 'array_with_invalid_format' => [ 'value' => [ @@ -85,7 +103,8 @@ public function beforeSaveDataProvider() 'country_id' => 'US', ], ], - 'expected' => serialize([]), + 'expected' => [], + 'encoded' => '[]' ], 'array_with_two_countries' => [ 'value' => [ @@ -99,12 +118,11 @@ public function beforeSaveDataProvider() ], '__empty' => "", ], - 'expected' => serialize( - [ - 'AF' => ['AE', 'VI'], - 'US' => ['AE', 'VI', 'MA'], - ] - ), + 'expected' => [ + 'AF' => ['AE', 'VI'], + 'US' => ['AE', 'VI', 'MA'], + ], + 'encoded' => '{"AF":["AE","VI"],"US":["AE","VI","MA"]}' ], 'array_with_two_same_countries' => [ 'value' => [ @@ -122,29 +140,38 @@ public function beforeSaveDataProvider() ], '__empty' => "", ], - 'expected' => serialize( - [ - 'AF' => ['AE', 'VI'], - 'US' => ['AE', 'VI', 'MA', 'OT'], - ] - ), + 'expected' => [ + 'AF' => ['AE', 'VI'], + 'US' => ['AE', 'VI', 'MA', 'OT'], + ], + 'encoded' => '{"AF":["AE","VI"],"US":["AE","VI","MA","OT"]}' ], ]; } /** * @dataProvider afterLoadDataProvider + * @param string $encodedValue + * @param array|null $value + * @param array $hashData + * @param array|null $expected */ - public function testAfterLoad($value, $hashData, $expected) + public function testAfterLoad($encodedValue, $value, array $hashData, $expected) { - $this->model->setValue($value); + $this->model->setValue($encodedValue); $index = 0; foreach ($hashData as $hash) { $this->mathRandomMock->expects(static::at($index)) ->method('getUniqueHash') ->willReturn($hash); - $index ++; + $index++; } + + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->with($encodedValue) + ->willReturn($value); + $this->model->afterLoad(); $this->assertEquals($expected, $this->model->getValue()); } @@ -157,34 +184,35 @@ public function afterLoadDataProvider() { return [ 'empty' => [ - 'value' => serialize([]), + 'encoded' => '[]', + 'value' => [], 'randomHash' => [], - 'expected' => [], + 'expected' => [] ], 'null' => [ + 'encoded' => '', 'value' => null, 'randomHash' => [], - 'expected' => null, + 'expected' => null ], - 'valid_data' => [ - 'value' => serialize( - [ - 'US' => ['AE', 'VI', 'MA'], - 'AF' => ['AE', 'MA'], - ] - ), + 'valid data' => [ + 'encoded' => '{"US":["AE","VI","MA"],"AF":["AE","MA"]}', + 'value' => [ + 'US' => ['AE', 'VI', 'MA'], + 'AF' => ['AE', 'MA'] + ], 'randomHash' => ['hash_1', 'hash_2'], 'expected' => [ 'hash_1' => [ 'country_id' => 'US', - 'cc_types' => ['AE', 'VI', 'MA'], + 'cc_types' => ['AE', 'VI', 'MA'] ], 'hash_2' => [ 'country_id' => 'AF', - 'cc_types' => ['AE', 'MA'], - ], + 'cc_types' => ['AE', 'MA'] + ] ] - ], + ] ]; } } diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Adminhtml/System/Config/CountryTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Adminhtml/System/Config/CountryTest.php index 3beaf139e5bc5..ccb8e7e8f6e17 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Adminhtml/System/Config/CountryTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Adminhtml/System/Config/CountryTest.php @@ -1,6 +1,6 @@ componentFactory = $this->getMockBuilder(TokenUiComponentInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->urlBuilder = $this->getMock(UrlInterface::class); + + $this->config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->setMethods(['getPayPalIcon']) + ->getMock(); + + $this->tokenUiComponentProvider = new TokenUiComponentProvider( + $this->componentFactory, + $this->urlBuilder, + $this->config + ); + } + + /** + * @covers \Magento\Braintree\Model\Ui\Adminhtml\PayPal\TokenUiComponentProvider::getComponentForToken + */ + public function testGetComponentForToken() + { + $nonceUrl = 'https://payment/adminhtml/nonce/url'; + $payerEmail = 'john.doe@test.com'; + $icon = [ + 'url' => 'https://payment/adminhtml/icon.png', + 'width' => 48, + 'height' => 32 + ]; + + $expected = [ + 'code' => 'vault', + 'nonceUrl' => $nonceUrl, + 'details' => [ + 'payerEmail' => $payerEmail, + 'icon' => $icon + ], + 'template' => 'vault.phtml' + ]; + + $this->config->expects(static::once()) + ->method('getPayPalIcon') + ->willReturn($icon); + + $paymentToken = $this->getMock(PaymentTokenInterface::class); + $paymentToken->expects(static::once()) + ->method('getTokenDetails') + ->willReturn('{"payerEmail":" ' . $payerEmail . '"}'); + $paymentToken->expects(static::once()) + ->method('getPublicHash') + ->willReturn('cmk32dl21l'); + + $this->urlBuilder->expects(static::once()) + ->method('getUrl') + ->willReturn($nonceUrl); + + $tokenComponent = $this->getMock(TokenUiComponentInterface::class); + $tokenComponent->expects(static::once()) + ->method('getConfig') + ->willReturn($expected); + + $this->componentFactory->expects(static::once()) + ->method('create') + ->willReturn($tokenComponent); + + $component = $this->tokenUiComponentProvider->getComponentForToken($paymentToken); + static::assertEquals($tokenComponent, $component); + static::assertEquals($expected, $component->getConfig()); + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/TokenUiComponentProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/TokenUiComponentProviderTest.php index d1665c71804cf..d39cd32425013 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/TokenUiComponentProviderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/TokenUiComponentProviderTest.php @@ -1,16 +1,16 @@ 'vault', 'nonceUrl' => $nonceUrl, 'details' => [ 'type' => $type, diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php index 04846f369eba9..1adc43a1ecd28 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Braintree/etc/adminhtml/di.xml b/app/code/Magento/Braintree/etc/adminhtml/di.xml index f252b977f20bd..c91b4edbd2e70 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/di.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/di.xml @@ -1,7 +1,7 @@ @@ -47,6 +47,7 @@ Magento\Braintree\Model\Ui\Adminhtml\TokenUiComponentProvider + Magento\Braintree\Model\Ui\Adminhtml\PayPal\TokenUiComponentProvider
    diff --git a/app/code/Magento/Braintree/etc/adminhtml/menu.xml b/app/code/Magento/Braintree/etc/adminhtml/menu.xml index ed73aa20cdb6f..43d12aa0bb995 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/menu.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/menu.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/etc/adminhtml/routes.xml b/app/code/Magento/Braintree/etc/adminhtml/routes.xml index 698664f02e6a3..0991aef948541 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/routes.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/routes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/etc/adminhtml/system.xml b/app/code/Magento/Braintree/etc/adminhtml/system.xml index e4f4e11983892..ef3cdc3f51256 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/system.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/system.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/etc/config.xml b/app/code/Magento/Braintree/etc/config.xml index 095a8419c8529..eaa233da109ce 100644 --- a/app/code/Magento/Braintree/etc/config.xml +++ b/app/code/Magento/Braintree/etc/config.xml @@ -1,7 +1,7 @@ @@ -71,7 +71,8 @@ BraintreePayPalVaultFacade - Vault Token (Braintree PayPal) + Stored Accounts (Braintree PayPal) + 1 diff --git a/app/code/Magento/Braintree/etc/di.xml b/app/code/Magento/Braintree/etc/di.xml index 849a3039fc361..6f596cc4a8daf 100644 --- a/app/code/Magento/Braintree/etc/di.xml +++ b/app/code/Magento/Braintree/etc/di.xml @@ -1,7 +1,7 @@ @@ -22,6 +22,7 @@ Magento\Braintree\Model\Ui\PayPal\ConfigProvider::PAYPAL_CODE BraintreePayPalInfo BraintreePayPalValueHandlerPool + BraintreePayPalValidatorPool BraintreePayPalCommandPool @@ -364,7 +365,7 @@ - + @@ -451,7 +452,7 @@ - + @@ -474,7 +475,7 @@ - + Magento\Braintree\Gateway\Config\Config @@ -487,6 +488,22 @@ + + + + + + Magento\Braintree\Gateway\Config\PayPal\Config + + + + + + BraintreePayPalCountryValidator + + + + @@ -527,4 +544,16 @@ + + + + 1 + 1 + 1 + 1 + 1 + 1 + + + diff --git a/app/code/Magento/Braintree/etc/events.xml b/app/code/Magento/Braintree/etc/events.xml index 6d76a626d15bd..2bf95bdbad178 100644 --- a/app/code/Magento/Braintree/etc/events.xml +++ b/app/code/Magento/Braintree/etc/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/etc/frontend/di.xml b/app/code/Magento/Braintree/etc/frontend/di.xml index cdd56e236a72b..781f985b4b3a8 100644 --- a/app/code/Magento/Braintree/etc/frontend/di.xml +++ b/app/code/Magento/Braintree/etc/frontend/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/etc/frontend/events.xml b/app/code/Magento/Braintree/etc/frontend/events.xml index df1db1420b5a1..e1bff1a20b238 100644 --- a/app/code/Magento/Braintree/etc/frontend/events.xml +++ b/app/code/Magento/Braintree/etc/frontend/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/etc/frontend/routes.xml b/app/code/Magento/Braintree/etc/frontend/routes.xml index 7ec5a0c1b097c..ad8b484ca30d6 100644 --- a/app/code/Magento/Braintree/etc/frontend/routes.xml +++ b/app/code/Magento/Braintree/etc/frontend/routes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/etc/frontend/sections.xml b/app/code/Magento/Braintree/etc/frontend/sections.xml index f87695a624730..add86f4cdb5cc 100644 --- a/app/code/Magento/Braintree/etc/frontend/sections.xml +++ b/app/code/Magento/Braintree/etc/frontend/sections.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/etc/module.xml b/app/code/Magento/Braintree/etc/module.xml index 2b7759fc7843b..b7d0957c2ef6d 100644 --- a/app/code/Magento/Braintree/etc/module.xml +++ b/app/code/Magento/Braintree/etc/module.xml @@ -1,12 +1,12 @@ - + diff --git a/app/code/Magento/Braintree/registration.php b/app/code/Magento/Braintree/registration.php index 33f9f68a4197b..d56ac32c07c96 100644 --- a/app/code/Magento/Braintree/registration.php +++ b/app/code/Magento/Braintree/registration.php @@ -1,6 +1,6 @@ @@ -9,4 +9,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Braintree/view/adminhtml/layout/braintree_report_index.xml b/app/code/Magento/Braintree/view/adminhtml/layout/braintree_report_index.xml index 30c334cd09464..396f86b903fd5 100644 --- a/app/code/Magento/Braintree/view/adminhtml/layout/braintree_report_index.xml +++ b/app/code/Magento/Braintree/view/adminhtml/layout/braintree_report_index.xml @@ -1,7 +1,7 @@ @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_index.xml b/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_index.xml index 571c5ededeb99..b5ce1fc9c6792 100644 --- a/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_index.xml +++ b/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_index.xml @@ -1,7 +1,7 @@ @@ -18,6 +18,10 @@ braintree_cc_vault Magento_Vault::form/vault.phtml + + braintree_paypal_vault + Magento_Vault::form/vault.phtml + @@ -18,6 +18,10 @@ braintree_cc_vault Magento_Vault::form/vault.phtml + + braintree_paypal_vault + Magento_Vault::form/vault.phtml + - \ No newline at end of file + diff --git a/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml b/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml index cd2fbcf3fec7a..50076da1a851f 100644 --- a/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml +++ b/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml @@ -1,6 +1,6 @@ getData(TokenUiComponentProviderInterface::COMPONENT_DETAILS); +$icon = $details['icon']; +$id = $block->escapeHtml($block->getData('id')); +?> +
    ", + "nonceUrl": "escapeUrl($block->getData('nonceUrl')); ?>" + } + }' id="payment_" class="admin__field"> +
    + + + escapeHtml($details['payerEmail']); ?> +
    +
    diff --git a/app/code/Magento/Braintree/view/adminhtml/templates/form/vault.phtml b/app/code/Magento/Braintree/view/adminhtml/templates/form/vault.phtml index 3811461884725..5414be0a571e9 100644 --- a/app/code/Magento/Braintree/view/adminhtml/templates/form/vault.phtml +++ b/app/code/Magento/Braintree/view/adminhtml/templates/form/vault.phtml @@ -1,13 +1,13 @@ getData('details'); +$details = $block->getData(TokenUiComponentProviderInterface::COMPONENT_DETAILS); $icon = $block->getData('icons')[$details['type']]; $id = $block->escapeHtml($block->getData('id')); ?> @@ -15,6 +15,7 @@ $id = $block->escapeHtml($block->getData('id')); "Magento_Braintree/js/vault": { "container": "payment_", "publicHash": "escapeHtml($block->getData(TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH)); ?>", + "code": "escapeHtml($block->getData('code')); ?>", "nonceUrl": "escapeUrl($block->getData('nonceUrl')); ?>" } }' id="payment_" class="admin__field"> diff --git a/app/code/Magento/Braintree/view/adminhtml/templates/grid/tooltip.phtml b/app/code/Magento/Braintree/view/adminhtml/templates/grid/tooltip.phtml index 7a07fe648f315..34c109e2d69f8 100644 --- a/app/code/Magento/Braintree/view/adminhtml/templates/grid/tooltip.phtml +++ b/app/code/Magento/Braintree/view/adminhtml/templates/grid/tooltip.phtml @@ -1,6 +1,6 @@ escapeHtml($block->getCode()); payment = new Braintree(config); }); //]]> - \ No newline at end of file + diff --git a/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml b/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml index 031ddca7a8707..7613bbcfd4081 100644 --- a/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml +++ b/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js b/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js index 93b2f9831ef35..2457e3582a15e 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ /*browser:true*/ diff --git a/app/code/Magento/Braintree/view/adminhtml/web/js/grid/filters/status.html b/app/code/Magento/Braintree/view/adminhtml/web/js/grid/filters/status.html index 68c1dbcee8f45..f69cbcc619846 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/grid/filters/status.html +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/grid/filters/status.html @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Braintree/view/adminhtml/web/js/grid/provider.js b/app/code/Magento/Braintree/view/adminhtml/web/js/grid/provider.js index ff0f8408b4079..8b1380f6029be 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/grid/provider.js +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/grid/provider.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js b/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js index fcff173e7fcd4..541542c83bc8f 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ /*browser:true*/ @@ -14,7 +14,8 @@ define([ return Class.extend({ defaults: { $selector: null, - selector: 'edit_form' + selector: 'edit_form', + $container: null }, /** @@ -25,17 +26,18 @@ define([ var self = this; self.$selector = $('#' + self.selector); + self.$container = $('#' + self.container); self.$selector.on( - 'setVaultNotActive', + 'setVaultNotActive.' + self.getCode(), function () { - self.$selector.off('submitOrder.braintree_vault'); + self.$selector.off('submitOrder.' + self.getCode()); } ); - this._super(); + self._super(); - this.initEventHandlers(); + self.initEventHandlers(); - return this; + return self; }, /** @@ -43,14 +45,14 @@ define([ * @returns {String} */ getCode: function () { - return 'braintree'; + return this.code; }, /** * Init event handlers */ initEventHandlers: function () { - $('#' + this.container).find('[name="payment[token_switcher]"]') + $(this.$container).find('[name="payment[token_switcher]"]') .on('click', this.selectPaymentMethod.bind(this)); }, @@ -66,7 +68,7 @@ define([ * Enable form event listeners */ enableEventListeners: function () { - this.$selector.on('submitOrder.braintree_vault', this.submitOrder.bind(this)); + this.$selector.on('submitOrder.' + this.getCode(), this.submitOrder.bind(this)); }, /** @@ -129,7 +131,7 @@ define([ this.createPublicHashSelector(); this.$selector.find('[name="payment[public_hash]"]').val(this.publicHash); - this.$selector.find('#braintree_nonce').val(nonce); + this.$container.find('#' + this.getNonceSelectorName()).val(nonce); }, /** @@ -138,16 +140,16 @@ define([ createPublicHashSelector: function () { var $input; - if (this.$selector.find('#braintree_nonce').size() === 0) { + if (this.$container.find('#' + this.getNonceSelectorName()).size() === 0) { $input = $('').attr( { type: 'hidden', - id: 'braintree_nonce', + id: this.getNonceSelectorName(), name: 'payment[payment_method_nonce]' } ); - $input.appendTo(this.$selector); + $input.appendTo(this.$container); $input.prop('disabled', false); } }, @@ -160,6 +162,14 @@ define([ alert({ content: message }); + }, + + /** + * Get selector name for nonce input + * @returns {String} + */ + getNonceSelectorName: function () { + return 'nonce_' + this.getCode(); } }); }); diff --git a/app/code/Magento/Braintree/view/adminhtml/web/styles.css b/app/code/Magento/Braintree/view/adminhtml/web/styles.css index 81378f636eb61..19a4f794fb428 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/styles.css +++ b/app/code/Magento/Braintree/view/adminhtml/web/styles.css @@ -1,8 +1,8 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ .braintree-section .heading {background: url("images/braintree_logo.png") no-repeat 0 50% / 18rem auto; padding-left: 20rem;} .braintree-section .button-container {float: right;} -.braintree-section .config-alt {background: url("images/braintree_allinone.png") no-repeat scroll 0 0 / 100% auto; height: 28px; margin: 0.5rem 0 0; width: 230px;} \ No newline at end of file +.braintree-section .config-alt {background: url("images/braintree_allinone.png") no-repeat scroll 0 0 / 100% auto; height: 28px; margin: 0.5rem 0 0; width: 230px;} diff --git a/app/code/Magento/Braintree/view/base/web/js/validator.js b/app/code/Magento/Braintree/view/base/web/js/validator.js index 8c878840ca10c..931774aaffa2d 100644 --- a/app/code/Magento/Braintree/view/base/web/js/validator.js +++ b/app/code/Magento/Braintree/view/base/web/js/validator.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ /*browser:true*/ diff --git a/app/code/Magento/Braintree/view/frontend/layout/braintree_paypal_review.xml b/app/code/Magento/Braintree/view/frontend/layout/braintree_paypal_review.xml index 19b7a795c2dc2..a5125861a048f 100644 --- a/app/code/Magento/Braintree/view/frontend/layout/braintree_paypal_review.xml +++ b/app/code/Magento/Braintree/view/frontend/layout/braintree_paypal_review.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/Braintree/view/frontend/layout/checkout_index_index.xml index a6b5b8795c46b..872cb7d51ce1f 100644 --- a/app/code/Magento/Braintree/view/frontend/layout/checkout_index_index.xml +++ b/app/code/Magento/Braintree/view/frontend/layout/checkout_index_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/view/frontend/layout/vault_cards_listaction.xml b/app/code/Magento/Braintree/view/frontend/layout/vault_cards_listaction.xml index c8cd4e3cc4085..1ab68abf1976d 100644 --- a/app/code/Magento/Braintree/view/frontend/layout/vault_cards_listaction.xml +++ b/app/code/Magento/Braintree/view/frontend/layout/vault_cards_listaction.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Braintree/view/frontend/requirejs-config.js b/app/code/Magento/Braintree/view/frontend/requirejs-config.js index 76391a81fe663..e12a4dd87043e 100644 --- a/app/code/Magento/Braintree/view/frontend/requirejs-config.js +++ b/app/code/Magento/Braintree/view/frontend/requirejs-config.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml b/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml index 35cb4617ec9ed..5fc4bf83cab28 100644 --- a/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml +++ b/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml @@ -1,6 +1,6 @@ @@ -109,8 +109,8 @@ -
    diff --git a/app/code/Magento/Bundle/etc/extension_attributes.xml b/app/code/Magento/Bundle/etc/extension_attributes.xml index 3819e892e6ff1..d23dfc71b39fc 100644 --- a/app/code/Magento/Bundle/etc/extension_attributes.xml +++ b/app/code/Magento/Bundle/etc/extension_attributes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/etc/frontend/di.xml b/app/code/Magento/Bundle/etc/frontend/di.xml index acaf67ac82e5e..084f681df7e03 100644 --- a/app/code/Magento/Bundle/etc/frontend/di.xml +++ b/app/code/Magento/Bundle/etc/frontend/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/etc/frontend/events.xml b/app/code/Magento/Bundle/etc/frontend/events.xml index ce21acda965de..d08cdb4bcc997 100644 --- a/app/code/Magento/Bundle/etc/frontend/events.xml +++ b/app/code/Magento/Bundle/etc/frontend/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/etc/module.xml b/app/code/Magento/Bundle/etc/module.xml index 982a33d00bc6b..878af07761b42 100644 --- a/app/code/Magento/Bundle/etc/module.xml +++ b/app/code/Magento/Bundle/etc/module.xml @@ -1,12 +1,12 @@ - + diff --git a/app/code/Magento/Bundle/etc/pdf.xml b/app/code/Magento/Bundle/etc/pdf.xml index 085e7946cb7d6..912aa1426efa1 100644 --- a/app/code/Magento/Bundle/etc/pdf.xml +++ b/app/code/Magento/Bundle/etc/pdf.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/etc/product_types.xml b/app/code/Magento/Bundle/etc/product_types.xml index c3f909afbeabe..6168189a3b4e3 100644 --- a/app/code/Magento/Bundle/etc/product_types.xml +++ b/app/code/Magento/Bundle/etc/product_types.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/etc/sales.xml b/app/code/Magento/Bundle/etc/sales.xml index 74e5647051dec..3094eb6bfecfa 100644 --- a/app/code/Magento/Bundle/etc/sales.xml +++ b/app/code/Magento/Bundle/etc/sales.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/etc/webapi.xml b/app/code/Magento/Bundle/etc/webapi.xml index 6e31986be7b8f..69124309687a3 100644 --- a/app/code/Magento/Bundle/etc/webapi.xml +++ b/app/code/Magento/Bundle/etc/webapi.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/etc/webapi_rest/di.xml b/app/code/Magento/Bundle/etc/webapi_rest/di.xml index acaf67ac82e5e..084f681df7e03 100644 --- a/app/code/Magento/Bundle/etc/webapi_rest/di.xml +++ b/app/code/Magento/Bundle/etc/webapi_rest/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/etc/webapi_soap/di.xml b/app/code/Magento/Bundle/etc/webapi_soap/di.xml index acaf67ac82e5e..084f681df7e03 100644 --- a/app/code/Magento/Bundle/etc/webapi_soap/di.xml +++ b/app/code/Magento/Bundle/etc/webapi_soap/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/registration.php b/app/code/Magento/Bundle/registration.php index 48ae2414286eb..53f3657ae0519 100644 --- a/app/code/Magento/Bundle/registration.php +++ b/app/code/Magento/Bundle/registration.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/adminhtml_order_shipment_view.xml b/app/code/Magento/Bundle/view/adminhtml/layout/adminhtml_order_shipment_view.xml index 34975692a948b..0091b151426fa 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/adminhtml_order_shipment_view.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/adminhtml_order_shipment_view.xml @@ -1,14 +1,14 @@ - + diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_bundle.xml b/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_bundle.xml index 14ab43a776bbb..d0fdb184355d7 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_bundle.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_bundle.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_new.xml b/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_new.xml index 251a2ddd68af6..a496ea7538923 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_new.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_new.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_view_type_bundle.xml b/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_view_type_bundle.xml index 370ef3cf8afca..15374cb987d36 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_view_type_bundle.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/catalog_product_view_type_bundle.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/customer_index_wishlist.xml b/app/code/Magento/Bundle/view/adminhtml/layout/customer_index_wishlist.xml index b7a0c9d03e16e..7e37070109921 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/customer_index_wishlist.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/customer_index_wishlist.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_new.xml b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_new.xml index f3962e20d4435..99fdab5a7e9f4 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_new.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_new.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml index f3962e20d4435..99fdab5a7e9f4 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_view.xml b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_view.xml index 5f2c852416e04..323aba1d186e6 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_view.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_creditmemo_view.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_new.xml b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_new.xml index 9b37d8286fdcb..b03ce7a9cb451 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_new.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_new.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_updateqty.xml b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_updateqty.xml index 9b37d8286fdcb..b03ce7a9cb451 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_updateqty.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_updateqty.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_view.xml b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_view.xml index 752031796631f..34c3470cf06a7 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_view.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_invoice_view.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_view.xml b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_view.xml index 2c6a8fe93caa9..62f0305194fa9 100644 --- a/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_view.xml +++ b/app/code/Magento/Bundle/view/adminhtml/layout/sales_order_view.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/catalog/product/edit/tab/attributes/extend.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/catalog/product/edit/tab/attributes/extend.phtml index 0dff5ef370825..84e87afd48180 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/catalog/product/edit/tab/attributes/extend.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/catalog/product/edit/tab/attributes/extend.phtml @@ -1,6 +1,6 @@ -decorateArray($block->getOptions()); ?> +decorateArray($block->getOptions(true)); ?>
    diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml index f74f4e3435c67..970b1c38642af 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/create/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/create/items/renderer.phtml index 8fddd0798f7b4..41923c4826432 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/create/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/create/items/renderer.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Bundle/view/adminhtml/web/css/bundle-product.css b/app/code/Magento/Bundle/view/adminhtml/web/css/bundle-product.css index 3503d6050e777..d5aed8a7d652e 100644 --- a/app/code/Magento/Bundle/view/adminhtml/web/css/bundle-product.css +++ b/app/code/Magento/Bundle/view/adminhtml/web/css/bundle-product.css @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/bundle-product.js b/app/code/Magento/Bundle/view/adminhtml/web/js/bundle-product.js index a4e47177bdf22..d0c143bde14bd 100644 --- a/app/code/Magento/Bundle/view/adminhtml/web/js/bundle-product.js +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/bundle-product.js @@ -1,23 +1,24 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -/*jshint browser:true jquery:true*/ + /*global FORM_KEY*/ /*global bSelection*/ /*global $H*/ define([ - "jquery", - "Magento_Catalog/js/product/weight-handler", - "Magento_Ui/js/modal/modal", - "jquery/ui", - "mage/translate", - "Magento_Theme/js/sortable", - "prototype" -], function($, weightHandler, modal){ + 'jquery', + 'Magento_Catalog/js/product/weight-handler', + 'Magento_Ui/js/modal/modal', + 'jquery/ui', + 'mage/translate', + 'Magento_Theme/js/sortable', + 'prototype' +], function ($, weightHandler) { 'use strict'; $.widget('mage.bundleProduct', { + /** @inheritdoc */ _create: function () { this._initOptionBoxes() ._initSortableSelections() @@ -26,7 +27,14 @@ define([ ._bindAddSelectionDialog() ._hideProductTypeSwitcher(); }, + + /** + * @return {Object} + * @private + */ _initOptionBoxes: function () { + var syncOptionTitle; + this.element.sortable({ axis: 'y', handle: '[data-role=draggable-handle]', @@ -35,7 +43,10 @@ define([ tolerance: 'pointer' }); - var syncOptionTitle = function (event) { + /** + * @param {jQuery.Event} event + */ + syncOptionTitle = function (event) { var originalValue = $(event.target).attr('data-original-value'), currentValue = $(event.target).val(), optionBoxTitle = $('.title > span', $(event.target).closest('.option-box')), @@ -48,17 +59,29 @@ define([ 'keyup .field-option-title input[name$="[title]"]': syncOptionTitle, 'paste .field-option-title input[name$="[title]"]': syncOptionTitle }); - + return this; }, + + /** + * @return {Object} + * @private + */ _initSortableSelections: function () { this.element.find('.option-box .form-list tbody').sortable({ axis: 'y', handle: '[data-role=draggable-handle]', - helper: function(event, ui) { - ui.children().each(function() { + + /** + * @param {jQuery.Event} event + * @param {jQuery} ui + * @return {jQuery} + */ + helper: function (event, ui) { + ui.children().each(function () { $(this).width($(this).width()); }); + return ui; }, update: this._updateSelectionsPositions, @@ -67,121 +90,172 @@ define([ return this; }, - _initCheckboxState: function(){ + + /** + * @return {Object} + * @private + */ + _initCheckboxState: function () { this.element.find('.is-required').each(function () { $(this).prop('checked', $(this).closest('.option-box').find('[name$="[required]"]').val() > 0); }); - + this.element.find('.is-user-defined-qty').each(function () { $(this).prop('checked', $(this).closest('.qty-box').find('.select').val() > 0); }); return this; }, + + /** + * @return {Object} + * @private + */ _bindAddSelectionDialog: function () { var widget = this; - this._on({'click .add-selection': function (event) { - var $optionBox = $(event.target).closest('.option-box'), - $selectionGrid = $optionBox.find('.selection-search').clone(), - optionIndex = $optionBox.attr('id').replace('bundle_option_', ''), - productIds = [], - productSkus = [], - selectedProductList = {}; - - $optionBox.find('[name$="[product_id]"]').each(function () { - if (!$(this).closest('tr').find('[name$="[delete]"]').val()) { - productIds.push($(this).val()); - productSkus.push($(this).closest('tr').find('.col-sku').text()); - } - }); - - bSelection.gridSelection.set(optionIndex, $H({})); - bSelection.gridRemoval = $H({}); - bSelection.gridSelectedProductSkus = productSkus; - - $selectionGrid.on('contentUpdated', bSelection.gridUpdateCallback); - $selectionGrid.on('change', '.col-id input', function () {//_on can't be used because of grid reloading - var tr = $(this).closest('tr'); - if ($(this).is(':checked')) { - selectedProductList[$(this).val()] = { - name: $.trim(tr.find('.col-name').html()), - sku: $.trim(tr.find('.col-sku').html()), - product_id: $(this).val(), - option_id: $('bundle_selection_id_' + optionIndex).val(), - selection_price_value: 0, - selection_qty: 1 - }; - } else { - delete selectedProductList[$(this).val()]; - } - }); - - $selectionGrid.modal({ - title: $optionBox.find('input[name$="[title]"]').val() === '' ? - $.mage.__('Add Products to New Option') : - $.mage.__('Add Products to Option "%1"') - .replace('%1',($('
    ').text($optionBox.find('input[name$="[title]"]').val()).html())), - modalClass: 'bundle', - type: 'slide', - closed: function(e, modal) { - modal.modal.remove(); - }, - buttons: [{ - text: $.mage.__('Add Selected Products'), - 'class': 'action-primary action-add', - click: function () { - $.each(selectedProductList, function() { - window.bSelection.addRow(optionIndex, this); - }); - bSelection.gridRemoval.each( - function(pair) { + + this._on({ + /** + * @param {jQuery.Event} event + */ + 'click .add-selection': function (event) { + var $optionBox = $(event.target).closest('.option-box'), + $selectionGrid = $optionBox.find('.selection-search').clone(), + optionIndex = $optionBox.attr('id').replace('bundle_option_', ''), + productIds = [], + productSkus = [], + selectedProductList = {}; + + $optionBox.find('[name$="[product_id]"]').each(function () { + if (!$(this).closest('tr').find('[name$="[delete]"]').val()) { + productIds.push($(this).val()); + productSkus.push($(this).closest('tr').find('.col-sku').text()); + } + }); + + bSelection.gridSelection.set(optionIndex, $H({})); + bSelection.gridRemoval = $H({}); + bSelection.gridSelectedProductSkus = productSkus; + + $selectionGrid.on('contentUpdated', bSelection.gridUpdateCallback); + $selectionGrid.on('change', '.col-id input', function () { + var tr = $(this).closest('tr'); + + if ($(this).is(':checked')) { + selectedProductList[$(this).val()] = { + name: $.trim(tr.find('.col-name').html()), + sku: $.trim(tr.find('.col-sku').html()), + 'product_id': $(this).val(), + 'option_id': $('bundle_selection_id_' + optionIndex).val(), + 'selection_price_value': 0, + 'selection_qty': 1 + }; + } else { + delete selectedProductList[$(this).val()]; + } + }); + + $selectionGrid.modal({ + title: $optionBox.find('input[name$="[title]"]').val() === '' ? + $.mage.__('Add Products to New Option') : + $.mage.__('Add Products to Option "%1"').replace( + '%1', + $('
    ').text($optionBox.find('input[name$="[title]"]').val()).html() + ), + modalClass: 'bundle', + type: 'slide', + + /** + * @param {jQuery.Event} e + * @param {Object} modalWindow + */ + closed: function (e, modalWindow) { + modalWindow.modal.remove(); + }, + buttons: [{ + text: $.mage.__('Add Selected Products'), + 'class': 'action-primary action-add', + + /** Click action. */ + click: function () { + $.each(selectedProductList, function () { + window.bSelection.addRow(optionIndex, this); + }); + bSelection.gridRemoval.each(function (pair) { $optionBox.find('.col-sku').filter(function () { return $.trim($(this).text()) === pair.key; // find row by SKU }).closest('tr').find('button.delete').trigger('click'); - } - ); - widget.refreshSortableElements(); - widget._updateSelectionsPositions.apply(widget.element); - $selectionGrid.modal('closeModal'); - } - }] - }); - $.ajax({ - url: bSelection.selectionSearchUrl, - dataType: 'html', - data: { - index: optionIndex, - products: productIds, - selected_products: productIds, - form_key: FORM_KEY - }, - success: function(data) { - $selectionGrid.html(data).modal('openModal'); - }, - context: $('body'), - showLoader: true - }); - }}); + }); + widget.refreshSortableElements(); + widget._updateSelectionsPositions.apply(widget.element); + $selectionGrid.modal('closeModal'); + } + }] + }); + $.ajax({ + url: bSelection.selectionSearchUrl, + dataType: 'html', + data: { + index: optionIndex, + products: productIds, + 'selected_products': productIds, + 'form_key': FORM_KEY + }, + + /** + * @param {*} data + */ + success: function (data) { + $selectionGrid.html(data).modal('openModal'); + }, + context: $('body'), + showLoader: true + }); + } + }); return this; }, + + /** + * @private + */ _hideProductTypeSwitcher: function () { weightHandler.hideWeightSwitcher(); }, + + /** + * @return {Object} + * @private + */ _bindCheckboxHandlers: function () { this._on({ + /** + * @param {jQuery.Event} event + */ 'change .is-required': function (event) { var $this = $(event.target); + $this.closest('.option-box').find('[name$="[required]"]').val($this.is(':checked') ? 1 : 0); }, + + /** + * @param {jQuery.Event} event + */ 'change .is-user-defined-qty': function (event) { var $this = $(event.target); + $this.closest('.qty-box').find('.select').val($this.is(':checked') ? 1 : 0); } }); return this; }, + + /** + * @return {Object} + * @private + */ _updateOptionBoxPositions: function () { $(this).find('[name^=bundle_options][name$="[position]"]').each(function (index) { $(this).val(index); @@ -189,6 +263,11 @@ define([ return this; }, + + /** + * @return {Object} + * @private + */ _updateSelectionsPositions: function () { $(this).find('[name^=bundle_selections][name$="[position]"]').each(function (index) { $(this).val(index); @@ -196,6 +275,11 @@ define([ return this; }, + + /** + * + * @return {Object} + */ refreshSortableElements: function () { this.element.sortable('refresh'); this._updateOptionBoxPositions.apply(this.element); diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/bundle-type-handler.js b/app/code/Magento/Bundle/view/adminhtml/web/js/bundle-type-handler.js index 12dda7d3a148c..7f4a57cb530da 100644 --- a/app/code/Magento/Bundle/view/adminhtml/web/js/bundle-type-handler.js +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/bundle-type-handler.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ /*jshint browser:true jquery:true expr:true*/ diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-checkbox.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-checkbox.js index b7a05076ae268..9c2432bade43f 100644 --- a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-checkbox.js +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-checkbox.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js new file mode 100644 index 0000000000000..6265394cf38e3 --- /dev/null +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js @@ -0,0 +1,59 @@ +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'underscore', + 'Magento_Ui/js/dynamic-rows/dynamic-rows-grid' +], function (_, dynamicRowsGrid) { + 'use strict'; + + return dynamicRowsGrid.extend({ + defaults: { + label: '', + columnsHeader: false, + columnsHeaderAfterRender: true, + addButton: false + }, + + /** + * Initialize elements from grid + * + * @param {Array} data + * + * @returns {Object} Chainable. + */ + initElements: function (data) { + var newData = this.getNewData(data), + recordIndex; + + this.parsePagesData(data); + + if (newData.length) { + if (this.insertData().length) { + recordIndex = data.length - newData.length - 1; + + _.each(newData, function (newRecord) { + this.processingAddChild(newRecord, ++recordIndex, newRecord[this.identificationProperty]); + }, this); + } + } + + return this; + }, + + /** + * Mapping value from grid + * + * @param {Array} data + */ + mappingValue: function (data) { + if (_.isEmpty(data)) { + return; + } + + this._super(); + } + }); +}); diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows.js new file mode 100644 index 0000000000000..4bbd1b3e4da20 --- /dev/null +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows.js @@ -0,0 +1,98 @@ +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'underscore', + 'mageUtils', + 'uiRegistry', + 'Magento_Ui/js/dynamic-rows/dynamic-rows' +], function (_, utils, registry, dynamicRows) { + 'use strict'; + + return dynamicRows.extend({ + defaults: { + label: '', + collapsibleHeader: true, + columnsHeader: false, + deleteProperty: false, + addButton: false + }, + + /** + * Set new data to dataSource, + * delete element + * + * @param {Array} data - record data + */ + _updateData: function (data) { + var elems = _.clone(this.elems()), + path, + dataArr, + optionBaseData; + + dataArr = this.recordData.splice(this.startIndex, this.recordData().length - this.startIndex); + dataArr.splice(0, this.pageSize); + elems = _.sortBy(this.elems(), function (elem) { + return ~~elem.index; + }); + + data.concat(dataArr).forEach(function (rec, idx) { + if (elems[idx]) { + elems[idx].recordId = rec[this.identificationProperty]; + } + + if (!rec.position) { + rec.position = this.maxPosition; + this.setMaxPosition(); + } + + path = this.dataScope + '.' + this.index + '.' + (this.startIndex + idx); + optionBaseData = _.pick(rec, function (value) { + return !_.isObject(value); + }); + this.source.set(path, optionBaseData); + this.source.set(path + '.bundle_button_proxy', []); + this.source.set(path + '.bundle_selections', []); + this.removeBundleItemsFromOption(idx); + _.each(rec['bundle_selections'], function (obj, index) { + this.source.set(path + '.bundle_button_proxy' + '.' + index, rec['bundle_button_proxy'][index]); + this.source.set(path + '.bundle_selections' + '.' + index, obj); + }, this); + }, this); + + this.elems(elems); + }, + + /** + * Removes nested dynamic-rows-grid rendered records from option + * + * @param {Number|String} index - element index + */ + removeBundleItemsFromOption: function (index) { + var bundleSelections = registry.get(this.name + '.' + index + '.' + this.bundleSelectionsName), + bundleSelectionsLength = (bundleSelections.elems() || []).length, + i; + + if (bundleSelectionsLength) { + for (i = 0; i < bundleSelectionsLength; i++) { + bundleSelections.elems()[0].destroy(); + } + } + }, + + /** + * {@inheritdoc} + */ + processingAddChild: function (ctx, index, prop) { + var recordIds = _.map(this.recordData(), function (rec) { + return parseInt(rec['record_id'], 10); + }), + maxRecordId = _.max(recordIds); + + prop = maxRecordId > -1 ? maxRecordId + 1 : prop; + this._super(ctx, index, prop); + } + }); +}); diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-input-type.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-input-type.js index 14dd426ed02aa..cee0489cc901a 100644 --- a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-input-type.js +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-input-type.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-option-qty.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-option-qty.js index b5c1d61d41f20..d5be0a19b597d 100644 --- a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-option-qty.js +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-option-qty.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/Bundle/view/base/layout/catalog_product_prices.xml b/app/code/Magento/Bundle/view/base/layout/catalog_product_prices.xml index 92d76cdfaa580..5f7d22ba2650b 100644 --- a/app/code/Magento/Bundle/view/base/layout/catalog_product_prices.xml +++ b/app/code/Magento/Bundle/view/base/layout/catalog_product_prices.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/base/templates/product/price/final_price.phtml b/app/code/Magento/Bundle/view/base/templates/product/price/final_price.phtml index 4c11b0ba1fe50..efd75677d3061 100644 --- a/app/code/Magento/Bundle/view/base/templates/product/price/final_price.phtml +++ b/app/code/Magento/Bundle/view/base/templates/product/price/final_price.phtml @@ -1,6 +1,6 @@ getTierPriceList(); ' . $tierPriceModel->getSavePercent($price['price']) . '%' + '' . round($price['percentage_value']) . '%' ); ?>
  • diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index 1a76acf174756..c6dcc9838f062 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -1,7 +1,8 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + define([ 'jquery', 'underscore', @@ -48,7 +49,10 @@ define([ priceBox = $(this.options.priceBoxSelector, form), qty = $(this.options.qtyFieldSelector, form); - if (priceBox.data('magePriceBox') && priceBox.priceBox('option') && priceBox.priceBox('option').priceConfig) { + if (priceBox.data('magePriceBox') && + priceBox.priceBox('option') && + priceBox.priceBox('option').priceConfig + ) { if (priceBox.priceBox('option').priceConfig.optionTemplate) { this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate); } @@ -78,7 +82,7 @@ define([ if (handler && handler instanceof Function) { changes = handler(bundleOption, this.options.optionConfig, this); } else { - changes = defaultGetOptionValue(bundleOption, this.options.optionConfig); + changes = defaultGetOptionValue(bundleOption, this.options.optionConfig);//eslint-disable-line } if (changes) { @@ -117,12 +121,13 @@ define([ */ _applyQtyFix: function applyQtyFix() { var config = this.options.optionConfig; + if (config.isFixedPrice) { _.each(config.options, function (option) { _.each(option.selections, function (item) { if (item.qty && item.qty !== 1) { _.each(item.prices, function (price) { - price.amount = price.amount / item.qty; + price.amount /= item.qty; }); } }); @@ -141,13 +146,13 @@ define([ var config = this.options, format = config.priceFormat, template = config.optionTemplate; + template = mageTemplate(template); options.filter('select').each(function (index, element) { var $element = $(element), optionId = utils.findOptionId($element), - optionName = $element.prop('name'), - optionType = $element.prop('type'), - optionConfig = config.optionConfig && config.optionConfig.options[optionId].selections; + optionConfig = config.optionConfig && config.optionConfig.options[optionId].selections, + value; $element.find('option').each(function (idx, option) { var $option, @@ -170,8 +175,8 @@ define([ prices = optionConfig[optionValue].prices; _.each(prices, function (price, type) { - var value = +(price.amount); - value += _.reduce(price.adjustments, function (sum, x) { + value = +price.amount; + value += _.reduce(price.adjustments, function (sum, x) {//eslint-disable-line return sum + x; }, 0); toTemplate.data[type] = { @@ -247,13 +252,17 @@ define([ if (optionValue) { optionQty = optionConfig[optionValue].qty || 0; canQtyCustomize = optionConfig[optionValue].customQty === '1'; - toggleQtyField(qtyField, optionQty, optionId, optionValue, canQtyCustomize); + toggleQtyField(qtyField, optionQty, optionId, optionValue, canQtyCustomize);//eslint-disable-line tempChanges = utils.deepClone(optionConfig[optionValue].prices); - tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig[optionValue]); - tempChanges = applyQty(tempChanges, optionQty); + tempChanges = applyTierPrice(//eslint-disable-line + tempChanges, + optionQty, + optionConfig[optionValue] + ); + tempChanges = applyQty(tempChanges, optionQty);//eslint-disable-line } else { tempChanges = {}; - toggleQtyField(qtyField, '0', optionId, optionValue, false); + toggleQtyField(qtyField, '0', optionId, optionValue, false);//eslint-disable-line } optionHash = 'bundle-option-' + optionName; changes[optionHash] = tempChanges; @@ -267,8 +276,8 @@ define([ optionHash = 'bundle-option-' + optionName + '##' + optionValueCode; optionQty = row.qty || 0; tempChanges = utils.deepClone(row.prices); - tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig); - tempChanges = applyQty(tempChanges, optionQty); + tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig);//eslint-disable-line + tempChanges = applyQty(tempChanges, optionQty);//eslint-disable-line changes[optionHash] = _.contains(optionValue, optionValueCode) ? tempChanges : {}; }); @@ -279,8 +288,8 @@ define([ optionHash = 'bundle-option-' + optionName + '##' + optionValue; optionQty = optionConfig[optionValue].qty || 0; tempChanges = utils.deepClone(optionConfig[optionValue].prices); - tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig); - tempChanges = applyQty(tempChanges, optionQty); + tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig);//eslint-disable-line + tempChanges = applyQty(tempChanges, optionQty);//eslint-disable-line changes[optionHash] = element.is(':checked') ? tempChanges : {}; selectedIds[optionId] = selectedIds[optionId] || []; @@ -295,9 +304,13 @@ define([ case 'hidden': optionHash = 'bundle-option-' + optionName + '##' + optionValue; optionQty = optionConfig[optionValue].qty || 0; + canQtyCustomize = optionConfig[optionValue].customQty === '1'; + qtyField = element.data('qtyField'); + qtyField.data('option', element); + toggleQtyField(qtyField, optionQty, optionId, optionValue, canQtyCustomize);//eslint-disable-line tempChanges = utils.deepClone(optionConfig[optionValue].prices); - tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig); - tempChanges = applyQty(tempChanges, optionQty); + tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig);//eslint-disable-line + tempChanges = applyQty(tempChanges, optionQty);//eslint-disable-line optionHash = 'bundle-option-' + optionName; changes[optionHash] = tempChanges; @@ -362,11 +375,9 @@ define([ lowest = false; _.each(tiers, function (tier, index) { - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - if (tier.price_qty > qty) { + if (tier['price_qty'] > qty) { return; } - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers if (tier.prices[magicKey].amount < oneItemPrice[magicKey].amount) { lowest = index; diff --git a/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_bundle.xml b/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_bundle.xml index 5ab962f0b91b5..2c66156cbc7a1 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_bundle.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_bundle.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_simple.xml b/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_simple.xml index e31437ec63963..fedd29f952b02 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_simple.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_simple.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/checkout_cart_configure_type_bundle.xml b/app/code/Magento/Bundle/view/frontend/layout/checkout_cart_configure_type_bundle.xml index e7128e45ddc7b..adb1b2911983b 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/checkout_cart_configure_type_bundle.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/checkout_cart_configure_type_bundle.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/checkout_cart_item_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/checkout_cart_item_renderers.xml index c5d613183e640..9abf0a4018980 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/checkout_cart_item_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/checkout_cart_item_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/checkout_onepage_review_item_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/checkout_onepage_review_item_renderers.xml index 09a27341fd5da..ddede2340995b 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/checkout_onepage_review_item_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/checkout_onepage_review_item_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/default.xml b/app/code/Magento/Bundle/view/frontend/layout/default.xml index a54d4b652b685..cb14616af5980 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/default.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/default.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_creditmemo_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_creditmemo_renderers.xml index 4db5f73f55bc5..991011db9fa08 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_creditmemo_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_creditmemo_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_invoice_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_invoice_renderers.xml index 1dba5769c0207..0e32c9fd9e816 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_invoice_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_invoice_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_renderers.xml index 6e1b90abf4d4e..927214fbcc174 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_shipment_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_shipment_renderers.xml index 7846245018a74..7463caa738083 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_shipment_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_email_order_shipment_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_order_creditmemo_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_order_creditmemo_renderers.xml index 7896e92bd34f4..c94b4957d267b 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_order_creditmemo_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_order_creditmemo_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_order_invoice_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_order_invoice_renderers.xml index 850e546a93a5a..d07959385bd9f 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_order_invoice_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_order_invoice_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_order_item_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_order_item_renderers.xml index 317b142514b9d..fb26de5bc2fdd 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_order_item_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_order_item_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_creditmemo_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_creditmemo_renderers.xml index dc5fb238dfd77..8c328c87a5c65 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_creditmemo_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_creditmemo_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_invoice_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_invoice_renderers.xml index 450f18d863ab5..57e39795df7da 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_invoice_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_invoice_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_renderers.xml index c4741ad6119f5..510168ac55e8a 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_shipment_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_shipment_renderers.xml index e3c91f1da5c20..f46b6260f3bd8 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_shipment_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_order_print_shipment_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/layout/sales_order_shipment_renderers.xml b/app/code/Magento/Bundle/view/frontend/layout/sales_order_shipment_renderers.xml index ce42a5466cf06..536953423a3f2 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/sales_order_shipment_renderers.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/sales_order_shipment_renderers.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Bundle/view/frontend/requirejs-config.js b/app/code/Magento/Bundle/view/frontend/requirejs-config.js index 9dd12524124a6..51ab4cab6bb2c 100644 --- a/app/code/Magento/Bundle/view/frontend/requirejs-config.js +++ b/app/code/Magento/Bundle/view/frontend/requirejs-config.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -12,4 +12,4 @@ var config = { productSummary: 'Magento_Bundle/js/product-summary' } } -}; \ No newline at end of file +}; diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/backbutton.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/backbutton.phtml index ddb483cf0220d..86cd52b6dc6fd 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/backbutton.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/backbutton.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/customize.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/customize.phtml index e72be96db1ab0..e3538ebd6caea 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/customize.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/customize.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/summary.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/summary.phtml index cb1de4e05e8a4..0fc7e6f9c8f06 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/summary.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/summary.phtml @@ -1,6 +1,6 @@ fieldset').focus(); }, - _hide: function() { + + /** + * @private + */ + _hide: function () { $('html, body').animate({ scrollTop: 0 }, 600); $(this.options.bundleOptionsContainer).slideUp(800); } }); - + return $.mage.slide; -}); \ No newline at end of file +}); diff --git a/app/code/Magento/BundleImportExport/Model/Export/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Export/Product/Type/Bundle.php index c9b6aa4c45ab3..5219de4b4a2fb 100644 --- a/app/code/Magento/BundleImportExport/Model/Export/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Export/Product/Type/Bundle.php @@ -1,6 +1,6 @@ parseAdditionalAttributes($dataRow['additional_attributes']); $dataRow['additional_attributes'] = $this->getNotBundleAttributes($additionalAttributes); } @@ -349,17 +346,38 @@ protected function cleanNotBundleAdditionalAttributes($dataRow) */ protected function getNotBundleAttributes($additionalAttributes) { - $cleanedAdditionalAttributes = ''; - foreach ($additionalAttributes as $attribute) { - list($attributeCode, $attributeValue) = explode(ImportProductModel::PAIR_NAME_VALUE_SEPARATOR, $attribute); - if (!in_array('bundle_' . $attributeCode, $this->getBundleColumns())) { - $cleanedAdditionalAttributes .= $attributeCode - . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR - . $attributeValue - . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR; + $filteredAttributes = []; + foreach ($additionalAttributes as $code => $value) { + if (!in_array('bundle_' . $code, $this->getBundleColumns())) { + $filteredAttributes[] = $code . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $value; } } + return implode(ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $filteredAttributes); + } - return rtrim($cleanedAdditionalAttributes, ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR); + /** + * Retrieves additional attributes as array code=>value. + * + * @param string $additionalAttributes + * @return array + */ + private function parseAdditionalAttributes($additionalAttributes) + { + $attributeNameValuePairs = explode(ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalAttributes); + $preparedAttributes = []; + $code = ''; + foreach ($attributeNameValuePairs as $attributeData) { + //process case when attribute has ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR inside its value + if (strpos($attributeData, ImportProductModel::PAIR_NAME_VALUE_SEPARATOR) === false) { + if (!$code) { + continue; + } + $preparedAttributes[$code] .= ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR . $attributeData; + continue; + } + list($code, $value) = explode(ImportProductModel::PAIR_NAME_VALUE_SEPARATOR, $attributeData, 2); + $preparedAttributes[$code] = $value; + } + return $preparedAttributes; } } diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index 2d0f1264b6b8a..d5548f61c2bf2 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -3,13 +3,12 @@ /** * Import entity of bundle product type * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\BundleImportExport\Model\Import\Product\Type; use \Magento\Bundle\Model\Product\Price as BundlePrice; -use \Magento\BundleImportExport\Model\Export\RowCustomizer; use \Magento\Catalog\Model\Product\Type\AbstractType; /** @@ -55,20 +54,6 @@ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abst */ const SELECTION_PRICE_TYPE_PERCENT = 1; - /** - * Instance of database adapter. - * - * @var \Magento\Framework\DB\Adapter\AdapterInterface - */ - protected $connection; - - /** - * Instance of application resource. - * - * @var \Magento\Framework\App\ResourceConnection - */ - protected $_resource; - /** * Array of cached options. * @@ -144,23 +129,6 @@ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abst 'multiselect' => 'multi', ]; - /** - * @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac - * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac - * @param \Magento\Framework\App\ResourceConnection $resource - * @param array $params - */ - public function __construct( - \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac, - \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac, - \Magento\Framework\App\ResourceConnection $resource, - array $params - ) { - parent::__construct($attrSetColFac, $prodAttrColFac, $resource, $params); - $this->_resource = $resource; - $this->connection = $resource->getConnection(\Magento\Framework\App\ResourceConnection::DEFAULT_CONNECTION); - } - /** * Parse selections. * diff --git a/app/code/Magento/BundleImportExport/Test/Unit/Model/Export/Product/RowCustomizerTest.php b/app/code/Magento/BundleImportExport/Test/Unit/Model/Export/Product/RowCustomizerTest.php index e7c313c56034b..0e67a2bc737c9 100644 --- a/app/code/Magento/BundleImportExport/Test/Unit/Model/Export/Product/RowCustomizerTest.php +++ b/app/code/Magento/BundleImportExport/Test/Unit/Model/Export/Product/RowCustomizerTest.php @@ -1,6 +1,6 @@ rowCustomizerMock->prepareData($this->productResourceCollection, [1]); - $attributes = 'attribute=1,sku_type=1,price_type=1,price_view=1,weight_type=1,values=values,shipment_type=1'; + $attributes = 'attribute=1,sku_type=1,attribute2="Text",price_type=1,price_view=1,weight_type=1,' + . 'values=values,shipment_type=1,attribute3=One,Two,Three'; $dataRow = [ 'sku' => 'sku1', 'additional_attributes' => $attributes @@ -186,7 +187,7 @@ public function testAddData() $preparedRow = $preparedData->addData($dataRow, 1); $expected = [ 'sku' => 'sku1', - 'additional_attributes' => 'attribute=1', + 'additional_attributes' => 'attribute=1,attribute2="Text",attribute3=One,Two,Three', 'bundle_price_type' => 'fixed', 'bundle_shipment_type' => 'separately', 'bundle_sku_type' => 'fixed', diff --git a/app/code/Magento/BundleImportExport/Test/Unit/Model/Import/Product/Type/BundleTest.php b/app/code/Magento/BundleImportExport/Test/Unit/Model/Import/Product/Type/BundleTest.php index 2379ad7f10701..fd25dc247c341 100644 --- a/app/code/Magento/BundleImportExport/Test/Unit/Model/Import/Product/Type/BundleTest.php +++ b/app/code/Magento/BundleImportExport/Test/Unit/Model/Import/Product/Type/BundleTest.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/BundleImportExport/etc/export.xml b/app/code/Magento/BundleImportExport/etc/export.xml index 04312d79f0260..c7fd951bfab53 100644 --- a/app/code/Magento/BundleImportExport/etc/export.xml +++ b/app/code/Magento/BundleImportExport/etc/export.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/BundleImportExport/etc/import.xml b/app/code/Magento/BundleImportExport/etc/import.xml index 2c23489002b1f..8daa5296a8c39 100644 --- a/app/code/Magento/BundleImportExport/etc/import.xml +++ b/app/code/Magento/BundleImportExport/etc/import.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/BundleImportExport/etc/module.xml b/app/code/Magento/BundleImportExport/etc/module.xml index d042b3bc7eb79..64f2c06b3c770 100644 --- a/app/code/Magento/BundleImportExport/etc/module.xml +++ b/app/code/Magento/BundleImportExport/etc/module.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/BundleImportExport/registration.php b/app/code/Magento/BundleImportExport/registration.php index b417e7d20b79e..b4f80e749f130 100644 --- a/app/code/Magento/BundleImportExport/registration.php +++ b/app/code/Magento/BundleImportExport/registration.php @@ -1,6 +1,6 @@ getEvent()->getObject(); + if (!is_object($object)) { + return; + } if ($this->config->getType() == \Magento\PageCache\Model\Config::VARNISH && $this->config->isEnabled()) { - $object = $observer->getEvent()->getObject(); - if ($object instanceof \Magento\Framework\DataObject\IdentityInterface) { - $tags = []; - $pattern = "((^|,)%s(,|$))"; - foreach ($object->getIdentities() as $tag) { - $tags[] = sprintf($pattern, $tag); - } - if (!empty($tags)) { - $this->purgeCache->sendPurgeRequest(implode('|', array_unique($tags))); - } + $bareTags = $this->getTagResolver()->getTags($object); + + $tags = []; + $pattern = "((^|,)%s(,|$))"; + foreach ($bareTags as $tag) { + $tags[] = sprintf($pattern, $tag); + } + if (!empty($tags)) { + $this->purgeCache->sendPurgeRequest(implode('|', array_unique($tags))); } + + } + } + + /** + * @deprecated + * @return \Magento\Framework\App\Cache\Tag\Resolver + */ + private function getTagResolver() + { + if ($this->tagResolver === null) { + $this->tagResolver = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\App\Cache\Tag\Resolver::class); } + return $this->tagResolver; } } diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php index 3cb6d8a054eb1..d4728fac0a17c 100644 --- a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php +++ b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php @@ -1,6 +1,6 @@ configMock = $this->getMock( \Magento\PageCache\Model\Config::class, ['getType', 'isEnabled'], @@ -39,6 +46,10 @@ protected function setUp() $this->configMock, $this->purgeCache ); + + $this->tagResolver = $this->getMock(\Magento\Framework\App\Cache\Tag\Resolver::class, [], [], '', false); + $helper->setBackwardCompatibleProperty($this->model, 'tagResolver', $this->tagResolver); + $this->observerMock = $this->getMock( \Magento\Framework\Event\Observer::class, ['getEvent'], @@ -65,10 +76,12 @@ public function testInvalidateVarnish() )->will( $this->returnValue(\Magento\PageCache\Model\Config::VARNISH) ); + $eventMock = $this->getMock(\Magento\Framework\Event::class, ['getObject'], [], '', false); $eventMock->expects($this->once())->method('getObject')->will($this->returnValue($this->observerObject)); $this->observerMock->expects($this->once())->method('getEvent')->will($this->returnValue($eventMock)); - $this->observerObject->expects($this->once())->method('getIdentities')->will($this->returnValue($tags)); + $this->tagResolver->expects($this->once())->method('getTags')->with($this->observerObject) + ->will($this->returnValue($tags)); $this->purgeCache->expects($this->once())->method('sendPurgeRequest')->with($pattern); $this->model->execute($this->observerMock); diff --git a/app/code/Magento/CacheInvalidate/composer.json b/app/code/Magento/CacheInvalidate/composer.json index cafccadb41ad8..c886e069b66e3 100644 --- a/app/code/Magento/CacheInvalidate/composer.json +++ b/app/code/Magento/CacheInvalidate/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cache-invalidate", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-page-cache": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/CacheInvalidate/etc/events.xml b/app/code/Magento/CacheInvalidate/etc/events.xml index 58ddeb64c9257..6103e661de55c 100644 --- a/app/code/Magento/CacheInvalidate/etc/events.xml +++ b/app/code/Magento/CacheInvalidate/etc/events.xml @@ -1,7 +1,7 @@ @@ -51,4 +51,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/CacheInvalidate/etc/module.xml b/app/code/Magento/CacheInvalidate/etc/module.xml index 7cc9d59df5959..b3277477fb62d 100644 --- a/app/code/Magento/CacheInvalidate/etc/module.xml +++ b/app/code/Magento/CacheInvalidate/etc/module.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/CacheInvalidate/registration.php b/app/code/Magento/CacheInvalidate/registration.php index 21f5baf8f333c..00ddee3f6776b 100644 --- a/app/code/Magento/CacheInvalidate/registration.php +++ b/app/code/Magento/CacheInvalidate/registration.php @@ -1,6 +1,6 @@ serializer = $serializer; + $this->captchaHelper = $captchaHelper; + } + /** * {@inheritdoc} */ public function execute() { $formId = $this->getRequest()->getPost('formId'); - $captchaModel = $this->_objectManager->get(\Magento\Captcha\Helper\Data::class)->getCaptcha($formId); + $captchaModel = $this->captchaHelper->getCaptcha($formId); $this->_view->getLayout()->createBlock( $captchaModel->getBlockName() )->setFormId( @@ -24,7 +50,7 @@ public function execute() )->setIsAjax( true )->toHtml(); - $this->getResponse()->representJson(json_encode(['imgSrc' => $captchaModel->getImgSrc()])); + $this->getResponse()->representJson($this->serializer->serialize(['imgSrc' => $captchaModel->getImgSrc()])); $this->_actionFlag->set('', self::FLAG_NO_POST_DISPATCH, true); } diff --git a/app/code/Magento/Captcha/Controller/Refresh/Index.php b/app/code/Magento/Captcha/Controller/Refresh/Index.php index eac5aef1946c7..ef757d1b2fc35 100644 --- a/app/code/Magento/Captcha/Controller/Refresh/Index.php +++ b/app/code/Magento/Captcha/Controller/Refresh/Index.php @@ -3,7 +3,7 @@ * Refreshes captcha and returns JSON encoded URL to image (AJAX action) * Example: {'imgSrc': 'http://example.com/media/captcha/67842gh187612ngf8s.png'} * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Captcha\Controller\Refresh; @@ -17,13 +17,25 @@ class Index extends \Magento\Framework\App\Action\Action */ protected $captchaHelper; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + protected $serializer; + /** * @param Context $context * @param \Magento\Captcha\Helper\Data $captchaHelper + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @throws \RuntimeException */ - public function __construct(Context $context, \Magento\Captcha\Helper\Data $captchaHelper) - { + public function __construct( + Context $context, + \Magento\Captcha\Helper\Data $captchaHelper, + \Magento\Framework\Serialize\Serializer\Json $serializer = null + ) { $this->captchaHelper = $captchaHelper; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct($context); } @@ -34,23 +46,19 @@ public function execute() { $formId = $this->_request->getPost('formId'); if (null === $formId) { - try { - $params = []; - $content = $this->_request->getContent(); - if ($content) { - $params = \Zend_Json::decode($content); - } - $formId = isset($params['formId']) ? $params['formId'] : null; - } catch (\Zend_Json_Exception $exception) { - $formId = null; + $params = []; + $content = $this->_request->getContent(); + if ($content) { + $params = $this->serializer->unserialize($content); } + $formId = isset($params['formId']) ? $params['formId'] : null; } $captchaModel = $this->captchaHelper->getCaptcha($formId); $captchaModel->generate(); $block = $this->_view->getLayout()->createBlock($captchaModel->getBlockName()); $block->setFormId($formId)->setIsAjax(true)->toHtml(); - $this->_response->representJson(json_encode(['imgSrc' => $captchaModel->getImgSrc()])); + $this->_response->representJson($this->serializer->serialize(['imgSrc' => $captchaModel->getImgSrc()])); $this->_actionFlag->set('', self::FLAG_NO_POST_DISPATCH, true); } } diff --git a/app/code/Magento/Captcha/Cron/DeleteExpiredImages.php b/app/code/Magento/Captcha/Cron/DeleteExpiredImages.php index 11ebf3438eaa2..af1a6e5687d03 100644 --- a/app/code/Magento/Captcha/Cron/DeleteExpiredImages.php +++ b/app/code/Magento/Captcha/Cron/DeleteExpiredImages.php @@ -1,6 +1,6 @@ helper = $helper; $this->sessionManager = $sessionManager; $this->resultJsonFactory = $resultJsonFactory; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); $this->formIds = $formIds; } @@ -53,7 +63,6 @@ public function __construct( * @param \Magento\Customer\Controller\Ajax\Login $subject * @param \Closure $proceed * @return $this - * @throws \Zend_Json_Exception * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -70,7 +79,7 @@ public function aroundExecute( $loginParams = []; $content = $request->getContent(); if ($content) { - $loginParams = \Zend_Json::decode($content); + $loginParams = $this->serializer->unserialize($content); } $username = isset($loginParams['username']) ? $loginParams['username'] : null; $captchaString = isset($loginParams[$captchaInputName]) ? $loginParams[$captchaInputName] : null; diff --git a/app/code/Magento/Captcha/Model/DefaultModel.php b/app/code/Magento/Captcha/Model/DefaultModel.php index c5a99de5ba178..a977ad6923611 100644 --- a/app/code/Magento/Captcha/Model/DefaultModel.php +++ b/app/code/Magento/Captcha/Model/DefaultModel.php @@ -1,16 +1,16 @@ */ -class DefaultModel extends \Zend_Captcha_Image implements \Magento\Captcha\Model\CaptchaInterface +class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model\CaptchaInterface { /** * Key in session for captcha code @@ -30,49 +30,50 @@ class DefaultModel extends \Zend_Captcha_Image implements \Magento\Captcha\Model /** * @var \Magento\Captcha\Helper\Data */ - protected $_captchaData; + protected $captchaData; /** * Captcha expire time * @var int */ - protected $_expiration; + protected $expiration; /** * Override default value to prevent a captcha cut off * @var int - * @see \Zend_Captcha_Image::$_fsize + * @see \Zend\Captcha\Image::$fsize */ - protected $_fsize = 22; + protected $fsize = 22; /** * Captcha form id * @var string */ - protected $_formId; + protected $formId; /** * @var \Magento\Captcha\Model\ResourceModel\LogFactory */ - protected $_resLogFactory; + protected $resLogFactory; /** * Overrides parent parameter as session comes in constructor. * * @var bool */ - protected $_keepSession = true; + protected $keepSession = true; /** * @var \Magento\Framework\Session\SessionManagerInterface */ - protected $_session; + protected $session; /** * @param \Magento\Framework\Session\SessionManagerInterface $session * @param \Magento\Captcha\Helper\Data $captchaData - * @param \Magento\Captcha\Model\ResourceModel\LogFactory $resLogFactory + * @param ResourceModel\LogFactory $resLogFactory * @param string $formId + * @throws \Zend\Captcha\Exception\ExtensionNotLoadedException */ public function __construct( \Magento\Framework\Session\SessionManagerInterface $session, @@ -80,10 +81,11 @@ public function __construct( \Magento\Captcha\Model\ResourceModel\LogFactory $resLogFactory, $formId ) { - $this->_session = $session; - $this->_captchaData = $captchaData; - $this->_resLogFactory = $resLogFactory; - $this->_formId = $formId; + parent::__construct(); + $this->session = $session; + $this->captchaData = $captchaData; + $this->resLogFactory = $resLogFactory; + $this->formId = $formId; } /** @@ -92,9 +94,9 @@ public function __construct( * @param string $key * @return string */ - protected function _getFormIdKey($key) + private function getFormIdKey($key) { - return $this->_formId . '_' . $key; + return $this->formId . '_' . $key; } /** @@ -115,19 +117,21 @@ public function getBlockName() */ public function isRequired($login = null) { - if ($this->_isUserAuth() && !$this->isShownToLoggedInUser() || !$this->_isEnabled() || !in_array( - $this->_formId, - $this->_getTargetForms() - ) + if ( + $this->isUserAuth() + && !$this->isShownToLoggedInUser() + || !$this->isEnabled() + || !in_array( + $this->formId, + $this->getTargetForms() + ) ) { return false; } - return $this->_isShowAlways() || $this->_isOverLimitAttempts( - $login - ) || $this->_session->getData( - $this->_getFormIdKey('show_captcha') - ); + return $this->isShowAlways() + || $this->isOverLimitAttempts($login) + || $this->session->getData($this->getFormIdKey('show_captcha')); } /** @@ -137,9 +141,9 @@ public function isRequired($login = null) */ public function isShownToLoggedInUser() { - $forms = (array)$this->_captchaData->getConfig('shown_to_logged_in_user'); + $forms = (array)$this->captchaData->getConfig('shown_to_logged_in_user'); foreach ($forms as $formId => $isShownToLoggedIn) { - if ($isShownToLoggedIn && $this->_formId == $formId) { + if ($isShownToLoggedIn && $this->formId == $formId) { return true; } } @@ -152,9 +156,9 @@ public function isShownToLoggedInUser() * @param string $login * @return bool */ - protected function _isOverLimitAttempts($login) + private function isOverLimitAttempts($login) { - return $this->_isOverLimitIpAttempt() || $this->_isOverLimitLoginAttempts($login); + return $this->isOverLimitIpAttempt() || $this->isOverLimitLoginAttempts($login); } /** @@ -162,9 +166,9 @@ protected function _isOverLimitAttempts($login) * * @return int */ - protected function _getAllowedAttemptsForSameLogin() + private function getAllowedAttemptsForSameLogin() { - return (int)$this->_captchaData->getConfig('failed_attempts_login'); + return (int)$this->captchaData->getConfig('failed_attempts_login'); } /** @@ -172,20 +176,20 @@ protected function _getAllowedAttemptsForSameLogin() * * @return int */ - protected function _getAllowedAttemptsFromSameIp() + private function getAllowedAttemptsFromSameIp() { - return (int)$this->_captchaData->getConfig('failed_attempts_ip'); + return (int)$this->captchaData->getConfig('failed_attempts_ip'); } /** - * Check is overlimit saved attempts from one ip + * Check is over limit saved attempts from one ip * * @return bool */ - protected function _isOverLimitIpAttempt() + private function isOverLimitIpAttempt() { - $countAttemptsByIp = $this->_getResourceModel()->countAttemptsByRemoteAddress(); - return $countAttemptsByIp >= $this->_getAllowedAttemptsFromSameIp(); + $countAttemptsByIp = $this->getResourceModel()->countAttemptsByRemoteAddress(); + return $countAttemptsByIp >= $this->getAllowedAttemptsFromSameIp(); } /** @@ -194,11 +198,11 @@ protected function _isOverLimitIpAttempt() * @param string $login * @return bool */ - protected function _isOverLimitLoginAttempts($login) + private function isOverLimitLoginAttempts($login) { if ($login != false) { - $countAttemptsByLogin = $this->_getResourceModel()->countAttemptsByUserLogin($login); - return $countAttemptsByLogin >= $this->_getAllowedAttemptsForSameLogin(); + $countAttemptsByLogin = $this->getResourceModel()->countAttemptsByUserLogin($login); + return $countAttemptsByLogin >= $this->getAllowedAttemptsForSameLogin(); } return false; } @@ -208,9 +212,9 @@ protected function _isOverLimitLoginAttempts($login) * * @return bool */ - protected function _isUserAuth() + private function isUserAuth() { - return $this->_session->isLoggedIn(); + return $this->session->isLoggedIn(); } /** @@ -220,7 +224,7 @@ protected function _isUserAuth() */ public function isCaseSensitive() { - return (string)$this->_captchaData->getConfig('case_sensitive'); + return (string)$this->captchaData->getConfig('case_sensitive'); } /** @@ -230,8 +234,8 @@ public function isCaseSensitive() */ public function getFont() { - $font = (string)$this->_captchaData->getConfig('font'); - $fonts = $this->_captchaData->getFonts(); + $font = (string)$this->captchaData->getConfig('font'); + $fonts = $this->captchaData->getFonts(); if (isset($fonts[$font])) { $fontPath = $fonts[$font]['path']; @@ -250,14 +254,14 @@ public function getFont() */ public function getExpiration() { - if (!$this->_expiration) { + if (!$this->expiration) { /** * as "timeout" configuration parameter specifies timeout in minutes - we multiply it on 60 to set * expiration in seconds */ - $this->_expiration = (int)$this->_captchaData->getConfig('timeout') * 60; + $this->expiration = (int)$this->captchaData->getConfig('timeout') * 60; } - return $this->_expiration; + return $this->expiration; } /** @@ -277,7 +281,7 @@ public function getTimeout() */ public function getImgDir() { - return $this->_captchaData->getImgDir(); + return $this->captchaData->getImgDir(); } /** @@ -287,7 +291,7 @@ public function getImgDir() */ public function getImgUrl() { - return $this->_captchaData->getImgUrl(); + return $this->captchaData->getImgUrl(); } /** @@ -299,7 +303,7 @@ public function getImgUrl() public function isCorrect($word) { $storedWord = $this->getWord(); - $this->_clearWord(); + $this->clearWord(); if (!$word || !$storedWord) { return false; @@ -330,9 +334,9 @@ public function getImgSrc() */ public function logAttempt($login) { - if ($this->_isEnabled() && in_array($this->_formId, $this->_getTargetForms())) { - $this->_getResourceModel()->logAttempt($login); - if ($this->_isOverLimitLoginAttempts($login)) { + if ($this->isEnabled() && in_array($this->formId, $this->getTargetForms())) { + $this->getResourceModel()->logAttempt($login); + if ($this->isOverLimitLoginAttempts($login)) { $this->setShowCaptchaInSession(true); } } @@ -351,19 +355,20 @@ public function setShowCaptchaInSession($value = true) $value = false; } - $this->_session->setData($this->_getFormIdKey('show_captcha'), $value); + $this->session->setData($this->getFormIdKey('show_captcha'), $value); } /** * Generate word used for captcha render * * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ - protected function _generateWord() + protected function generateWord() { $word = ''; - $symbols = $this->_getSymbols(); - $wordLen = $this->_getWordLen(); + $symbols = $this->getSymbols(); + $wordLen = $this->getWordLen(); for ($i = 0; $i < $wordLen; $i++) { $word .= $symbols[array_rand($symbols)]; } @@ -375,21 +380,22 @@ protected function _generateWord() * * @return array */ - protected function _getSymbols() + private function getSymbols() { - return str_split((string)$this->_captchaData->getConfig('symbols')); + return str_split((string)$this->captchaData->getConfig('symbols')); } /** * Returns length for generating captcha word. This value may be dynamic. * * @return int + * @throws \Magento\Framework\Exception\LocalizedException */ - protected function _getWordLen() + public function getWordLen() { $from = 0; $to = 0; - $length = (string)$this->_captchaData->getConfig('length'); + $length = (string)$this->captchaData->getConfig('length'); if (!is_numeric($length)) { if (preg_match('/(\d+)-(\d+)/', $length, $matches)) { $from = (int)$matches[1]; @@ -413,22 +419,22 @@ protected function _getWordLen() * * @return bool */ - protected function _isShowAlways() + private function isShowAlways() { - if ((string)$this->_captchaData->getConfig('mode') == \Magento\Captcha\Helper\Data::MODE_ALWAYS) { + if ((string)$this->captchaData->getConfig('mode') == \Magento\Captcha\Helper\Data::MODE_ALWAYS) { return true; } - if ((string)$this->_captchaData->getConfig( - 'mode' - ) == \Magento\Captcha\Helper\Data::MODE_AFTER_FAIL && $this->_getAllowedAttemptsForSameLogin() == 0 + if ( + (string)$this->captchaData->getConfig('mode') == \Magento\Captcha\Helper\Data::MODE_AFTER_FAIL + && $this->getAllowedAttemptsForSameLogin() == 0 ) { return true; } - $alwaysFor = $this->_captchaData->getConfig('always_for'); + $alwaysFor = $this->captchaData->getConfig('always_for'); foreach ($alwaysFor as $nodeFormId => $isAlwaysFor) { - if ($isAlwaysFor && $this->_formId == $nodeFormId) { + if ($isAlwaysFor && $this->formId == $nodeFormId) { return true; } } @@ -441,9 +447,9 @@ protected function _isShowAlways() * * @return bool */ - protected function _isEnabled() + private function isEnabled() { - return (string)$this->_captchaData->getConfig('enable'); + return (string)$this->captchaData->getConfig('enable'); } /** @@ -453,9 +459,9 @@ protected function _isEnabled() * * @return array */ - protected function _getTargetForms() + private function getTargetForms() { - $formsString = (string)$this->_captchaData->getConfig('forms'); + $formsString = (string)$this->captchaData->getConfig('forms'); return explode(',', $formsString); } @@ -466,7 +472,7 @@ protected function _getTargetForms() */ public function getWord() { - $sessionData = $this->_session->getData($this->_getFormIdKey(self::SESSION_WORD)); + $sessionData = $this->session->getData($this->getFormIdKey(self::SESSION_WORD)); return time() < $sessionData['expires'] ? $sessionData['data'] : null; } @@ -476,13 +482,13 @@ public function getWord() * @param string $word * @return $this */ - protected function _setWord($word) + protected function setWord($word) { - $this->_session->setData( - $this->_getFormIdKey(self::SESSION_WORD), + $this->session->setData( + $this->getFormIdKey(self::SESSION_WORD), ['data' => $word, 'expires' => time() + $this->getTimeout()] ); - $this->_word = $word; + $this->word = $word; return $this; } @@ -491,20 +497,21 @@ protected function _setWord($word) * * @return $this */ - protected function _clearWord() + private function clearWord() { - $this->_session->unsetData($this->_getFormIdKey(self::SESSION_WORD)); - $this->_word = null; + $this->session->unsetData($this->getFormIdKey(self::SESSION_WORD)); + $this->word = null; return $this; } /** * Override function to generate less curly captcha that will not cut off * - * @see \Zend_Captcha_Image::_randomSize() + * @see \Zend\Captcha\Image::_randomSize() * @return int + * @throws \Magento\Framework\Exception\LocalizedException */ - protected function _randomSize() + protected function randomSize() { return \Magento\Framework\Math\Random::getRandomNumber(280, 300) / 100; } @@ -516,8 +523,11 @@ protected function _randomSize() * * Now deleting old captcha images make crontab script * @see \Magento\Captcha\Cron\DeleteExpiredImages::execute + * + * Added SuppressWarnings since this method is declared in parent class and we can not use other method name. + * @SuppressWarnings(PHPMD.ShortMethodName) */ - protected function _gc() + protected function gc() { //do nothing } @@ -527,8 +537,8 @@ protected function _gc() * * @return \Magento\Captcha\Model\ResourceModel\Log */ - protected function _getResourceModel() + private function getResourceModel() { - return $this->_resLogFactory->create(); + return $this->resLogFactory->create(); } } diff --git a/app/code/Magento/Captcha/Model/ResourceModel/Log.php b/app/code/Magento/Captcha/Model/ResourceModel/Log.php index c38e01e61e41f..f746f1aa9d330 100644 --- a/app/code/Magento/Captcha/Model/ResourceModel/Log.php +++ b/app/code/Magento/Captcha/Model/ResourceModel/Log.php @@ -1,6 +1,6 @@ 1, 'updated_at' => $this->_coreDate->gmtDate() ], - ['count' => new \Zend_Db_Expr('count+1'), 'updated_at'] + ['count' => new \Zend\Db\Sql\Expression('count+1'), 'updated_at'] ); } $ip = $this->_remoteAddress->getRemoteAddress(); @@ -91,7 +92,7 @@ public function logAttempt($login) 'count' => 1, 'updated_at' => $this->_coreDate->gmtDate() ], - ['count' => new \Zend_Db_Expr('count+1'), 'updated_at'] + ['count' => new \Zend\Db\Sql\Expression('count+1'), 'updated_at'] ); } return $this; @@ -102,6 +103,7 @@ public function logAttempt($login) * * @param string $login * @return $this + * @throws \Magento\Framework\Exception\LocalizedException */ public function deleteUserAttempts($login) { @@ -126,6 +128,7 @@ public function deleteUserAttempts($login) * Get count attempts by ip * * @return null|int + * @throws \Magento\Framework\Exception\LocalizedException */ public function countAttemptsByRemoteAddress() { @@ -152,6 +155,7 @@ public function countAttemptsByRemoteAddress() * * @param string $login * @return null|int + * @throws \Magento\Framework\Exception\LocalizedException */ public function countAttemptsByUserLogin($login) { @@ -176,6 +180,7 @@ public function countAttemptsByUserLogin($login) * Delete attempts with expired in update_at time * * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function deleteOldAttempts() { diff --git a/app/code/Magento/Captcha/Observer/CaptchaStringResolver.php b/app/code/Magento/Captcha/Observer/CaptchaStringResolver.php index ff72c7d86a065..b7b8da326ef85 100644 --- a/app/code/Magento/Captcha/Observer/CaptchaStringResolver.php +++ b/app/code/Magento/Captcha/Observer/CaptchaStringResolver.php @@ -1,6 +1,6 @@ viewMock = $this->getMock(\Magento\Framework\App\ViewInterface::class); $this->layoutMock = $this->getMock(\Magento\Framework\View\LayoutInterface::class); $this->flagMock = $this->getMock(\Magento\Framework\App\ActionFlag::class, [], [], '', false); + $this->serializerMock = $this->getMock( + \Magento\Framework\Serialize\Serializer\Json::class, + [], + [], + '', + false + ); $this->contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->requestMock)); $this->contextMock->expects($this->any())->method('getView')->will($this->returnValue($this->viewMock)); @@ -69,7 +81,11 @@ protected function setUp() $this->contextMock->expects($this->any())->method('getActionFlag')->will($this->returnValue($this->flagMock)); $this->viewMock->expects($this->any())->method('getLayout')->will($this->returnValue($this->layoutMock)); - $this->model = new \Magento\Captcha\Controller\Refresh\Index($this->contextMock, $this->captchaHelperMock); + $this->model = new \Magento\Captcha\Controller\Refresh\Index( + $this->contextMock, + $this->captchaHelperMock, + $this->serializerMock + ); } /** @@ -80,6 +96,7 @@ protected function setUp() public function testExecute($formId, $callsNumber) { $content = ['formId' => $formId]; + $imgSource = ['imgSrc' => 'source']; $blockMethods = ['setFormId', 'setIsAjax', 'toHtml']; $blockMock = $this->getMock(\Magento\Captcha\Block\Captcha::class, $blockMethods, [], '', false); @@ -97,8 +114,12 @@ public function testExecute($formId, $callsNumber) $blockMock->expects($this->any())->method('setFormId')->with($formId)->will($this->returnValue($blockMock)); $blockMock->expects($this->any())->method('setIsAjax')->with(true)->will($this->returnValue($blockMock)); $blockMock->expects($this->once())->method('toHtml'); - $this->responseMock->expects($this->once())->method('representJson')->with(json_encode(['imgSrc' => 'source'])); + $this->responseMock->expects($this->once())->method('representJson')->with(json_encode($imgSource)); $this->flagMock->expects($this->once())->method('set')->with('', 'no-postDispatch', true); + $this->serializerMock->expects($this->exactly($callsNumber)) + ->method('unserialize')->will($this->returnValue($content)); + $this->serializerMock->expects($this->once()) + ->method('serialize')->will($this->returnValue(json_encode($imgSource))); $this->model->execute(); } diff --git a/app/code/Magento/Captcha/Test/Unit/Cron/DeleteExpiredImagesTest.php b/app/code/Magento/Captcha/Test/Unit/Cron/DeleteExpiredImagesTest.php index 4bdcceef3a6b1..45f90452bd1fb 100644 --- a/app/code/Magento/Captcha/Test/Unit/Cron/DeleteExpiredImagesTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Cron/DeleteExpiredImagesTest.php @@ -1,6 +1,6 @@ captchaHelperMock->expects($this->once())->method('getCaptcha') ->with('user_login')->will($this->returnValue($this->captchaMock)); $this->formIds = ['user_login']; + $this->serializerMock = $this->getMock( + \Magento\Framework\Serialize\Serializer\Json::class, + [], + [], + '', + false + ); $this->model = new \Magento\Captcha\Model\Customer\Plugin\AjaxLogin( $this->captchaHelperMock, $this->sessionManagerMock, $this->jsonFactoryMock, - $this->formIds + $this->formIds, + $this->serializerMock ); } @@ -92,11 +105,12 @@ public function testAroundExecute() { $username = 'name'; $captchaString = 'string'; - $requestContent = json_encode([ + $requestData = [ 'username' => $username, 'captcha_string' => $captchaString, 'captcha_form_id' => $this->formIds[0] - ]); + ]; + $requestContent = json_encode($requestData); $this->requestMock->expects($this->once())->method('getContent')->will($this->returnValue($requestContent)); $this->captchaMock->expects($this->once())->method('isRequired')->with($username) @@ -104,6 +118,7 @@ public function testAroundExecute() $this->captchaMock->expects($this->once())->method('logAttempt')->with($username); $this->captchaMock->expects($this->once())->method('isCorrect')->with($captchaString) ->will($this->returnValue(true)); + $this->serializerMock->expects(($this->once()))->method('unserialize')->will($this->returnValue($requestData)); $closure = function () { return 'result'; @@ -115,11 +130,12 @@ public function testAroundExecuteIncorrectCaptcha() { $username = 'name'; $captchaString = 'string'; - $requestContent = json_encode([ + $requestData = [ 'username' => $username, 'captcha_string' => $captchaString, 'captcha_form_id' => $this->formIds[0] - ]); + ]; + $requestContent = json_encode($requestData); $this->requestMock->expects($this->once())->method('getContent')->will($this->returnValue($requestContent)); $this->captchaMock->expects($this->once())->method('isRequired')->with($username) @@ -127,6 +143,7 @@ public function testAroundExecuteIncorrectCaptcha() $this->captchaMock->expects($this->once())->method('logAttempt')->with($username); $this->captchaMock->expects($this->once())->method('isCorrect') ->with($captchaString)->will($this->returnValue(false)); + $this->serializerMock->expects(($this->once()))->method('unserialize')->will($this->returnValue($requestData)); $this->sessionManagerMock->expects($this->once())->method('setUsername')->with($username); $this->jsonFactoryMock->expects($this->once())->method('create') @@ -147,7 +164,10 @@ public function testAroundExecuteIncorrectCaptcha() */ public function testAroundExecuteCaptchaIsNotRequired($username, $requestContent) { - $this->requestMock->expects($this->once())->method('getContent')->will($this->returnValue($requestContent)); + $this->requestMock->expects($this->once())->method('getContent') + ->will($this->returnValue(json_encode($requestContent))); + $this->serializerMock->expects(($this->once()))->method('unserialize') + ->will($this->returnValue($requestContent)); $this->captchaMock->expects($this->once())->method('isRequired')->with($username) ->will($this->returnValue(false)); @@ -168,11 +188,11 @@ public function aroundExecuteCaptchaIsNotRequired() return [ [ 'username' => 'name', - 'requestContent' => json_encode(['username' => 'name', 'captcha_string' => 'string']), + 'requestData' => ['username' => 'name', 'captcha_string' => 'string'], ], [ 'username' => null, - 'requestContent' => json_encode(['captcha_string' => 'string']), + 'requestData' => ['captcha_string' => 'string'], ], ]; } diff --git a/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php b/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php index a85941e113520..649bce192614b 100644 --- a/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Captcha/etc/adminhtml/events.xml b/app/code/Magento/Captcha/etc/adminhtml/events.xml index 984e5e9e29f4e..7fcadbfd8f2ff 100644 --- a/app/code/Magento/Captcha/etc/adminhtml/events.xml +++ b/app/code/Magento/Captcha/etc/adminhtml/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/adminhtml/routes.xml b/app/code/Magento/Captcha/etc/adminhtml/routes.xml index e06b87beef772..6b6b6717c489a 100644 --- a/app/code/Magento/Captcha/etc/adminhtml/routes.xml +++ b/app/code/Magento/Captcha/etc/adminhtml/routes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/adminhtml/system.xml b/app/code/Magento/Captcha/etc/adminhtml/system.xml index dc4c737597cce..88f0ae27f91c7 100644 --- a/app/code/Magento/Captcha/etc/adminhtml/system.xml +++ b/app/code/Magento/Captcha/etc/adminhtml/system.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/config.xml b/app/code/Magento/Captcha/etc/config.xml index a068485910a77..d969626d73144 100644 --- a/app/code/Magento/Captcha/etc/config.xml +++ b/app/code/Magento/Captcha/etc/config.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/crontab.xml b/app/code/Magento/Captcha/etc/crontab.xml index 846a15cbdbec6..d3d6e30e1a03a 100644 --- a/app/code/Magento/Captcha/etc/crontab.xml +++ b/app/code/Magento/Captcha/etc/crontab.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/crontab/di.xml b/app/code/Magento/Captcha/etc/crontab/di.xml index fd57ded2fb92b..f3086b469842b 100644 --- a/app/code/Magento/Captcha/etc/crontab/di.xml +++ b/app/code/Magento/Captcha/etc/crontab/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/di.xml b/app/code/Magento/Captcha/etc/di.xml index 0bb7660f27a6b..db624420ba647 100644 --- a/app/code/Magento/Captcha/etc/di.xml +++ b/app/code/Magento/Captcha/etc/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/events.xml b/app/code/Magento/Captcha/etc/events.xml index 274058ec98e82..4223c4a2a3256 100644 --- a/app/code/Magento/Captcha/etc/events.xml +++ b/app/code/Magento/Captcha/etc/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/frontend/di.xml b/app/code/Magento/Captcha/etc/frontend/di.xml index d15f8d5914998..209f9beb71a04 100644 --- a/app/code/Magento/Captcha/etc/frontend/di.xml +++ b/app/code/Magento/Captcha/etc/frontend/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/frontend/events.xml b/app/code/Magento/Captcha/etc/frontend/events.xml index e1441f0311ee8..dfa0d1b428557 100644 --- a/app/code/Magento/Captcha/etc/frontend/events.xml +++ b/app/code/Magento/Captcha/etc/frontend/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/etc/frontend/routes.xml b/app/code/Magento/Captcha/etc/frontend/routes.xml index 255f4551daf5d..d4bbe64821a91 100644 --- a/app/code/Magento/Captcha/etc/frontend/routes.xml +++ b/app/code/Magento/Captcha/etc/frontend/routes.xml @@ -1,7 +1,7 @@ @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Captcha/etc/module.xml b/app/code/Magento/Captcha/etc/module.xml index 1703142e54133..698604928afb6 100644 --- a/app/code/Magento/Captcha/etc/module.xml +++ b/app/code/Magento/Captcha/etc/module.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/registration.php b/app/code/Magento/Captcha/registration.php index a8fce947e6697..488ac412a8926 100644 --- a/app/code/Magento/Captcha/registration.php +++ b/app/code/Magento/Captcha/registration.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Captcha/view/adminhtml/layout/adminhtml_auth_login.xml b/app/code/Magento/Captcha/view/adminhtml/layout/adminhtml_auth_login.xml index 3cb5ffbbf5ae3..8c093257f1790 100644 --- a/app/code/Magento/Captcha/view/adminhtml/layout/adminhtml_auth_login.xml +++ b/app/code/Magento/Captcha/view/adminhtml/layout/adminhtml_auth_login.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/view/adminhtml/templates/default.phtml b/app/code/Magento/Captcha/view/adminhtml/templates/default.phtml index 1246f1f4f5df5..37d2ed6fe1238 100644 --- a/app/code/Magento/Captcha/view/adminhtml/templates/default.phtml +++ b/app/code/Magento/Captcha/view/adminhtml/templates/default.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Captcha/view/frontend/layout/contact_index_index.xml b/app/code/Magento/Captcha/view/frontend/layout/contact_index_index.xml index 9e31eea8aaeba..1460d8fac6974 100644 --- a/app/code/Magento/Captcha/view/frontend/layout/contact_index_index.xml +++ b/app/code/Magento/Captcha/view/frontend/layout/contact_index_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/view/frontend/layout/customer_account_create.xml b/app/code/Magento/Captcha/view/frontend/layout/customer_account_create.xml index 573af66d5bd31..cd72cc5857b83 100644 --- a/app/code/Magento/Captcha/view/frontend/layout/customer_account_create.xml +++ b/app/code/Magento/Captcha/view/frontend/layout/customer_account_create.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/view/frontend/layout/customer_account_edit.xml b/app/code/Magento/Captcha/view/frontend/layout/customer_account_edit.xml index 875479c49954c..9700e88006f10 100644 --- a/app/code/Magento/Captcha/view/frontend/layout/customer_account_edit.xml +++ b/app/code/Magento/Captcha/view/frontend/layout/customer_account_edit.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/view/frontend/layout/customer_account_forgotpassword.xml b/app/code/Magento/Captcha/view/frontend/layout/customer_account_forgotpassword.xml index dc92c7c3548bc..1f25fa040b591 100644 --- a/app/code/Magento/Captcha/view/frontend/layout/customer_account_forgotpassword.xml +++ b/app/code/Magento/Captcha/view/frontend/layout/customer_account_forgotpassword.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/view/frontend/layout/customer_account_login.xml b/app/code/Magento/Captcha/view/frontend/layout/customer_account_login.xml index bcabf0adccc26..3a24e44fd1afe 100644 --- a/app/code/Magento/Captcha/view/frontend/layout/customer_account_login.xml +++ b/app/code/Magento/Captcha/view/frontend/layout/customer_account_login.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/view/frontend/layout/default.xml b/app/code/Magento/Captcha/view/frontend/layout/default.xml index 9d6a234514855..43a770c54c0ca 100644 --- a/app/code/Magento/Captcha/view/frontend/layout/default.xml +++ b/app/code/Magento/Captcha/view/frontend/layout/default.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Captcha/view/frontend/requirejs-config.js b/app/code/Magento/Captcha/view/frontend/requirejs-config.js index 72f7d627b8707..ff1d9f1acc7b1 100644 --- a/app/code/Magento/Captcha/view/frontend/requirejs-config.js +++ b/app/code/Magento/Captcha/view/frontend/requirejs-config.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -9,4 +9,4 @@ var config = { captcha: 'Magento_Captcha/captcha' } } -}; \ No newline at end of file +}; diff --git a/app/code/Magento/Captcha/view/frontend/templates/default.phtml b/app/code/Magento/Captcha/view/frontend/templates/default.phtml index 92c008a0e186a..e028ea19fa8b4 100644 --- a/app/code/Magento/Captcha/view/frontend/templates/default.phtml +++ b/app/code/Magento/Captcha/view/frontend/templates/default.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/Api/AttributeSetFinderInterface.php b/app/code/Magento/Catalog/Api/AttributeSetFinderInterface.php index 4dabce697b30f..c7df36623ff83 100644 --- a/app/code/Magento/Catalog/Api/AttributeSetFinderInterface.php +++ b/app/code/Magento/Catalog/Api/AttributeSetFinderInterface.php @@ -1,7 +1,7 @@ (int) Entity identified or entity link field. + * 'value' => (float) Special price value. + * 'store_id' => (int) Store Id. + * 'sku' => (string) Product SKU. + * 'price_from' => (string) Special price from date value in UTC. + * 'price_to' => (string) Special price to date value in UTC. + * ] + */ + public function get(array $skus); + + /** + * Update product special prices. + * + * @param array $prices + * $prices = [ + * 'entity_id' => (int) Entity identified or entity link field. Required. + * 'attribute_id' => (int) Special price attribute Id. Required. + * 'store_id' => (int) Store Id. Required. + * 'value' => (float) Special price value. Required. + * 'price_from' => (string) Special price from date value in Y-m-d H:i:s format in UTC. Optional. + * 'price_to' => (string) Special price to date value in Y-m-d H:i:s format in UTC. Optional. + * ]; + * @return bool + * @throws \Magento\Framework\Exception\CouldNotSaveException Thrown if error occurred during price save. + */ + public function update(array $prices); + + /** + * Delete product special prices. + * + * @param array $prices + * $prices = [ + * 'entity_id' => (int) Entity identified or entity link field. Required. + * 'attribute_id' => (int) Special price attribute Id. Required. + * 'store_id' => (int) Store Id. Required. + * 'value' => (float) Special price value. Required. + * 'price_from' => (string) Special price from date value in Y-m-d H:i:s format in UTC. Optional. + * 'price_to' => (string) Special price to date value in Y-m-d H:i:s format in UTC. Optional. + * ]; + * @return bool + * @throws \Magento\Framework\Exception\CouldNotDeleteException Thrown if error occurred during price delete. + */ + public function delete(array $prices); +} diff --git a/app/code/Magento/Catalog/Api/SpecialPriceStorageInterface.php b/app/code/Magento/Catalog/Api/SpecialPriceStorageInterface.php new file mode 100644 index 0000000000000..fe234ab675b3f --- /dev/null +++ b/app/code/Magento/Catalog/Api/SpecialPriceStorageInterface.php @@ -0,0 +1,49 @@ +serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); + } + /** * @return $this */ @@ -19,6 +47,7 @@ public function _construct() /** * @return string + * @deprecated */ public function getProductsJSON() { @@ -30,6 +59,6 @@ public function getProductsJSON() $result[$id] = $product->toArray(['qty', 'position']); } } - return $result ? \Zend_Json::encode($result) : '{}'; + return $result ? $this->serializer->serialize($result) : '{}'; } } diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts.php index b6746106aea14..c76835456bf10 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts.php @@ -1,6 +1,6 @@ getTypeInstance()->hasRequiredOptions($product)) { + if (!$product->getTypeInstance()->isPossibleBuyFromList($product)) { if (!isset($additional['_escape'])) { $additional['_escape'] = true; } diff --git a/app/code/Magento/Catalog/Block/Product/AwareInterface.php b/app/code/Magento/Catalog/Block/Product/AwareInterface.php index de9564075531c..deec15b976ba3 100644 --- a/app/code/Magento/Catalog/Block/Product/AwareInterface.php +++ b/app/code/Magento/Catalog/Block/Product/AwareInterface.php @@ -1,6 +1,6 @@ _productCollection === null) { - $layer = $this->getLayer(); - /* @var $layer \Magento\Catalog\Model\Layer */ - if ($this->getShowRootCategory()) { - $this->setCategoryId($this->_storeManager->getStore()->getRootCategoryId()); - } - - // if this is a product view page - if ($this->_coreRegistry->registry('product')) { - // get collection of categories this product is associated with - $categories = $this->_coreRegistry->registry('product') - ->getCategoryCollection()->setPage(1, 1) - ->load(); - // if the product is associated with any category - if ($categories->count()) { - // show products from this category - $this->setCategoryId(current($categories->getIterator())); - } - } - - $origCategory = null; - if ($this->getCategoryId()) { - try { - $category = $this->categoryRepository->get($this->getCategoryId()); - } catch (NoSuchEntityException $e) { - $category = null; - } - - if ($category) { - $origCategory = $layer->getCurrentCategory(); - $layer->setCurrentCategory($category); - } - } - $this->_productCollection = $layer->getProductCollection(); - - $this->prepareSortableFieldsByCategory($layer->getCurrentCategory()); - - if ($origCategory) { - $layer->setCurrentCategory($origCategory); - } + $this->_productCollection = $this->initializeProductCollection(); } return $this->_productCollection; @@ -170,39 +145,9 @@ public function getMode() */ protected function _beforeToHtml() { - $toolbar = $this->getToolbarBlock(); - - // called prepare sortable parameters $collection = $this->_getProductCollection(); - - // use sortable parameters - $orders = $this->getAvailableOrders(); - if ($orders) { - $toolbar->setAvailableOrders($orders); - } - $sort = $this->getSortBy(); - if ($sort) { - $toolbar->setDefaultOrder($sort); - } - $dir = $this->getDefaultDirection(); - if ($dir) { - $toolbar->setDefaultDirection($dir); - } - $modes = $this->getModes(); - if ($modes) { - $toolbar->setModes($modes); - } - - // set collection to toolbar and apply sort - $toolbar->setCollection($collection); - - $this->setChild('toolbar', $toolbar); - $this->_eventManager->dispatch( - 'catalog_block_product_list_collection', - ['collection' => $this->_getProductCollection()] - ); - - $this->_getProductCollection()->load(); + $this->configureToolbar($this->getToolbarBlock(), $collection); + $collection->load(); return parent::_beforeToHtml(); } @@ -210,7 +155,7 @@ protected function _beforeToHtml() /** * Retrieve Toolbar block * - * @return \Magento\Catalog\Block\Product\ProductList\Toolbar + * @return Toolbar */ public function getToolbarBlock() { @@ -379,4 +324,107 @@ protected function getPriceRender() { return $this->getLayout()->getBlock('product.price.render.default'); } + + /** + * Configures product collection from a layer and returns its instance. + * + * Also in the scope of a product collection configuration, this method initiates configuration of Toolbar. + * The reason to do this is because we have a bunch of legacy code + * where Toolbar configures several options of a collection and therefore this block depends on the Toolbar. + * + * This dependency leads to a situation where Toolbar sometimes called to configure a product collection, + * and sometimes not. + * + * To unify this behavior and prevent potential bugs this dependency is explicitly called + * when product collection initialized. + * + * @return Collection + */ + private function initializeProductCollection() + { + $layer = $this->getLayer(); + /* @var $layer \Magento\Catalog\Model\Layer */ + if ($this->getShowRootCategory()) { + $this->setCategoryId($this->_storeManager->getStore()->getRootCategoryId()); + } + + // if this is a product view page + if ($this->_coreRegistry->registry('product')) { + // get collection of categories this product is associated with + $categories = $this->_coreRegistry->registry('product') + ->getCategoryCollection()->setPage(1, 1) + ->load(); + // if the product is associated with any category + if ($categories->count()) { + // show products from this category + $this->setCategoryId(current($categories->getIterator())); + } + } + + $origCategory = null; + if ($this->getCategoryId()) { + try { + $category = $this->categoryRepository->get($this->getCategoryId()); + } catch (NoSuchEntityException $e) { + $category = null; + } + + if ($category) { + $origCategory = $layer->getCurrentCategory(); + $layer->setCurrentCategory($category); + } + } + $collection = $layer->getProductCollection(); + + $this->prepareSortableFieldsByCategory($layer->getCurrentCategory()); + + if ($origCategory) { + $layer->setCurrentCategory($origCategory); + } + + $toolbar = $this->getToolbarBlock(); + $this->configureToolbar($toolbar, $collection); + + $this->_eventManager->dispatch( + 'catalog_block_product_list_collection', + ['collection' => $collection] + ); + + return $collection; + } + + /** + * Configures the Toolbar block with options from this block and configured product collection. + * + * The purpose of this method is the one-way sharing of different sorting related data + * between this block, which is responsible for product list rendering, + * and the Toolbar block, whose responsibility is a rendering of these options. + * + * @param ProductList\Toolbar $toolbar + * @param Collection $collection + * @return void + */ + private function configureToolbar(Toolbar $toolbar, Collection $collection) + { + // use sortable parameters + $orders = $this->getAvailableOrders(); + if ($orders) { + $toolbar->setAvailableOrders($orders); + } + $sort = $this->getSortBy(); + if ($sort) { + $toolbar->setDefaultOrder($sort); + } + $dir = $this->getDefaultDirection(); + if ($dir) { + $toolbar->setDefaultDirection($dir); + } + $modes = $this->getModes(); + if ($modes) { + $toolbar->setModes($modes); + } + // set collection to toolbar and apply sort + $toolbar->setCollection($collection); + $this->setChild('toolbar', $toolbar); + } } diff --git a/app/code/Magento/Catalog/Block/Product/NewProduct.php b/app/code/Magento/Catalog/Block/Product/NewProduct.php index 73a9df1bf3822..11bb639ac2a5b 100644 --- a/app/code/Magento/Catalog/Block/Product/NewProduct.php +++ b/app/code/Magento/Catalog/Block/Product/NewProduct.php @@ -1,6 +1,6 @@ $product->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat() - ]; + ]; return $this->_jsonEncoder->encode($config); } $tierPrices = []; $tierPricesList = $product->getPriceInfo()->getPrice('tier_price')->getTierPriceList(); foreach ($tierPricesList as $tierPrice) { - $tierPrices[] = $this->priceCurrency->convert($tierPrice['price']->getValue()); + $tierPrices[] = $tierPrice['price']->getValue(); } $config = [ - 'productId' => $product->getId(), + 'productId' => $product->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat(), - 'prices' => [ - 'oldPrice' => [ - 'amount' => $this->priceCurrency->convert( - $product->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue() - ), + 'prices' => [ + 'oldPrice' => [ + 'amount' => $product->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue(), 'adjustments' => [] ], - 'basePrice' => [ - 'amount' => $this->priceCurrency->convert( - $product->getPriceInfo()->getPrice('final_price')->getAmount()->getBaseAmount() - ), + 'basePrice' => [ + 'amount' => $product->getPriceInfo()->getPrice('final_price')->getAmount()->getBaseAmount(), 'adjustments' => [] ], 'finalPrice' => [ - 'amount' => $this->priceCurrency->convert( - $product->getPriceInfo()->getPrice('final_price')->getAmount()->getValue() - ), + 'amount' => $product->getPriceInfo()->getPrice('final_price')->getAmount()->getValue(), 'adjustments' => [] ] ], - 'idSuffix' => '_clone', - 'tierPrices' => $tierPrices + 'idSuffix' => '_clone', + 'tierPrices' => $tierPrices ]; $responseObject = new \Magento\Framework\DataObject(); diff --git a/app/code/Magento/Catalog/Block/Product/View/AbstractView.php b/app/code/Magento/Catalog/Block/Product/View/AbstractView.php index c8cc393c66006..5daaff14ed733 100644 --- a/app/code/Magento/Catalog/Block/Product/View/AbstractView.php +++ b/app/code/Magento/Catalog/Block/Product/View/AbstractView.php @@ -1,6 +1,6 @@ setData( 'medium_image_url', - $this->_imageHelper->init($product, 'product_page_image_medium') - ->constrainOnly(true)->keepAspectRatio(true)->keepFrame(false) + $this->_imageHelper->init($product, 'product_page_image_medium_no_frame') ->setImageFile($image->getFile()) ->getUrl() ); $image->setData( 'large_image_url', - $this->_imageHelper->init($product, 'product_page_image_large') - ->constrainOnly(true)->keepAspectRatio(true)->keepFrame(false) + $this->_imageHelper->init($product, 'product_page_image_large_no_frame') ->setImageFile($image->getFile()) ->getUrl() ); diff --git a/app/code/Magento/Catalog/Block/Product/View/Options.php b/app/code/Magento/Catalog/Block/Product/View/Options.php index 349352513bec6..5f045fdacfc3f 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options.php @@ -1,6 +1,6 @@ getRequest()->getParam('id', false); + $categoryId = $this->resolveCategoryId(); $storeId = (int)$this->getRequest()->getParam('store'); $category = $this->_objectManager->create(\Magento\Catalog\Model\Category::class); $category->setStoreId($storeId); @@ -62,6 +62,18 @@ protected function _initCategory($getRootInstead = false) return $category; } + /** + * Resolve Category Id (from get or from post) + * + * @return int + */ + private function resolveCategoryId() + { + $categoryId = (int)$this->getRequest()->getParam('id', false); + + return $categoryId ?: (int)$this->getRequest()->getParam('entity_id', false); + } + /** * Build response for ajax request * diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Add.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Add.php index 663bf6a5b0915..29ffcdf1682f8 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Add.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Add.php @@ -1,7 +1,7 @@ ajaxRequestResponse($category, $resultPage); } + $resultPageTitle = $categoryId ? $category->getName() . ' (ID: ' . $categoryId . ')' : __('Categories'); $resultPage->setActiveMenu('Magento_Catalog::catalog_categories'); $resultPage->getConfig()->getTitle()->prepend(__('Categories')); - $resultPage->getConfig()->getTitle()->prepend($categoryId ? $category->getName() : __('Categories')); + $resultPage->getConfig()->getTitle()->prepend($resultPageTitle); $resultPage->addBreadcrumb(__('Manage Catalog Categories'), __('Manage Categories')); $block = $resultPage->getLayout()->getBlock('catalog.wysiwyg.js'); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Grid.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Grid.php index 4b2fa125ac137..0eef32b191abc 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Grid.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Grid.php @@ -1,7 +1,7 @@ _request->getParam('param_name', 'image'); + try { - $result = $this->imageUploader->saveFileToTmpDir('image'); + $result = $this->imageUploader->saveFileToTmpDir($imageId); $result['cookie'] = [ 'name' => $this->_getSession()->getName(), diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php index 5640873acbd6b..7c02a5daa5d43 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php @@ -1,7 +1,7 @@ messageManager->addSuccess(__('You moved the category')); + $this->messageManager->addSuccess(__('You moved the category.')); } $block->setMessages($this->messageManager->getMessages(true)); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/RefreshPath.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/RefreshPath.php index 4202d21089563..f8aa64e6bdc1e 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/RefreshPath.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/RefreshPath.php @@ -1,7 +1,7 @@ resultRawFactory = $resultRawFactory; $this->resultJsonFactory = $resultJsonFactory; $this->layoutFactory = $layoutFactory; $this->storeManager = $storeManager; + $this->eavConfig = $eavConfig + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Eav\Model\Config::class); } /** * Filter category data * + * @deprecated * @param array $rawData * @return array */ protected function _filterCategoryPostData(array $rawData) { $data = $rawData; - // @todo It is a workaround to prevent saving this data in category model and it has to be refactored in future if (isset($data['image']) && is_array($data['image'])) { if (!empty($data['image']['delete'])) { $data['image'] = null; @@ -126,7 +136,7 @@ public function execute() $this->storeManager->setCurrentStore($store->getCode()); $parentId = isset($categoryPostData['parent']) ? $categoryPostData['parent'] : null; if ($categoryPostData) { - $category->addData($this->_filterCategoryPostData($categoryPostData)); + $category->addData($categoryPostData); if ($isNewCategory) { $parentCategory = $this->getParentCategory($parentId, $storeId); $category->setPath($parentCategory->getPath()); @@ -248,18 +258,30 @@ public function execute() } /** - * Image data preprocessing + * Sets image attribute data to false if image was removed * * @param array $data - * * @return array */ public function imagePreprocessing($data) { - if (empty($data['image'])) { - unset($data['image']); - $data['image']['delete'] = true; + $entityType = $this->eavConfig->getEntityType(CategoryAttributeInterface::ENTITY_TYPE_CODE); + + foreach ($entityType->getAttributeCollection() as $attributeModel) { + $attributeCode = $attributeModel->getAttributeCode(); + $backendModel = $attributeModel->getBackend(); + + if (isset($data[$attributeCode])) { + continue; + } + + if (!$backendModel instanceof \Magento\Catalog\Model\Category\Attribute\Backend\Image) { + continue; + } + + $data[$attributeCode] = false; } + return $data; } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/SuggestCategories.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/SuggestCategories.php index 9ab7e84cead06..2e34cf244ef9a 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/SuggestCategories.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/SuggestCategories.php @@ -1,7 +1,7 @@ attributeHelper->getStoreWebsiteId($storeId) ); if (!$stockItemDo->getProductId()) { - $inventoryData[] = $productId; + $inventoryData['product_id'] = $productId; } $stockItemId = $stockItemDo->getId(); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Validate.php index 803e2672f4249..51855cf0618ce 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Validate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Validate.php @@ -1,7 +1,7 @@ getSearchCriteriaBuilder() ->addFilter('attribute_set_id', $attributeSet->getAttributeSetId()) ->addFilter('attribute_group_code', $groupCode) - ->addSortOrder($this->getSortOrderBuilder()->setAscendingDirection()->create()) ->setPageSize(1) ->create(); @@ -252,18 +245,6 @@ private function getSearchCriteriaBuilder() return $this->searchCriteriaBuilder; } - /** - * @return SortOrderBuilder - */ - private function getSortOrderBuilder() - { - if (null === $this->sortOrderBuilder) { - $this->sortOrderBuilder = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Api\SortOrderBuilder::class); - } - return $this->sortOrderBuilder; - } - /** * @return AttributeManagementInterface */ diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/AlertsPriceGrid.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/AlertsPriceGrid.php index 7a0190d99db51..3b370edc27008 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/AlertsPriceGrid.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/AlertsPriceGrid.php @@ -1,7 +1,7 @@ $checkboxValue) { - if (!$checkboxValue) { - unset($productData['website_ids'][$websiteId]); - } - } + $productData['website_ids'] = $this->filterWebsiteIds($productData['website_ids']); $wasLockedMedia = false; if ($product->isLockedAttribute('media')) { @@ -199,6 +194,9 @@ public function initializeFromData(\Magento\Catalog\Model\Product $product, arra $customOptions = []; foreach ($options as $customOptionData) { if (empty($customOptionData['is_delete'])) { + if (empty($customOptionData['option_id'])) { + $customOptionData['option_id'] = null; + } if (isset($customOptionData['values'])) { $customOptionData['values'] = array_filter($customOptionData['values'], function ($valueData) { return empty($valueData['is_delete']); @@ -206,7 +204,6 @@ public function initializeFromData(\Magento\Catalog\Model\Product $product, arra } $customOption = $this->getCustomOptionFactory()->create(['data' => $customOptionData]); $customOption->setProductSku($product->getSku()); - $customOption->setOptionId(null); $customOptions[] = $customOption; } } @@ -255,7 +252,7 @@ protected function setProductLinks(\Magento\Catalog\Model\Product $product) foreach ($linkTypes as $linkType => $readonly) { if (isset($links[$linkType]) && !$readonly) { - foreach ((array) $links[$linkType] as $linkData) { + foreach ((array)$links[$linkType] as $linkData) { if (empty($linkData['id'])) { continue; } @@ -321,9 +318,11 @@ public function mergeProductOptions($productOptions, $overwriteOptions) if (isset($option['values']) && isset($overwriteOptions[$optionId]['values'])) { foreach ($option['values'] as $valueIndex => $value) { - $valueId = $value['option_type_id']; - $value = $this->overwriteValue($valueId, $value, $overwriteOptions[$optionId]['values']); - $option['values'][$valueIndex] = $value; + if (isset($value['option_type_id'])) { + $valueId = $value['option_type_id']; + $value = $this->overwriteValue($valueId, $value, $overwriteOptions[$optionId]['values']); + $option['values'][$valueIndex] = $value; + } } } @@ -347,6 +346,9 @@ private function overwriteValue($optionId, $option, $overwriteOptions) foreach ($overwriteOptions[$optionId] as $fieldName => $overwrite) { if ($overwrite && isset($option[$fieldName]) && isset($option['default_' . $fieldName])) { $option[$fieldName] = $option['default_' . $fieldName]; + if ('title' == $fieldName) { + $option['is_delete_store_title'] = 1; + } } } } @@ -415,4 +417,23 @@ private function getDateTimeFilter() } return $this->dateTimeFilter; } + + /** + * Remove ids of non selected websites from $websiteIds array and return filtered data + * $websiteIds parameter expects array with website ids as keys and 1 (selected) or 0 (non selected) as values + * Only one id (default website ID) will be set to $websiteIds array when the single store mode is turned on + * + * @param array $websiteIds + * @return array + */ + private function filterWebsiteIds($websiteIds) + { + if (!$this->storeManager->isSingleStoreMode()) { + $websiteIds = array_filter((array)$websiteIds); + } else { + $websiteIds[$this->storeManager->getWebsite(true)->getId()] = 1; + } + + return $websiteIds; + } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/HandlerFactory.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/HandlerFactory.php index f282df484b076..d720c26a8a905 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/HandlerFactory.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/HandlerFactory.php @@ -1,6 +1,6 @@ getAllIds(); $storeId = (int) $this->getRequest()->getParam('store', 0); $status = (int) $this->getRequest()->getParam('status'); + $filters = (array)$this->getRequest()->getParam('filters', []); + + if (isset($filters['store_id'])) { + $storeId = (int)$filters['store_id']; + } try { $this->_validateMassStatus($productIds, $status); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php index a5153a7342674..62fc127b43a3d 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php @@ -1,7 +1,7 @@ save(); $this->messageManager->addSuccess(__('You saved the attribute set.')); + } catch (\Magento\Framework\Exception\AlreadyExistsException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + $hasError = true; } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addError($e->getMessage()); $hasError = true; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/SetGrid.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/SetGrid.php index c0f7246f334f0..b9226a622f598 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/SetGrid.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/SetGrid.php @@ -1,7 +1,7 @@ addPageLayoutHandles(['type' => $parentType]); + $page->addPageLayoutHandles(['type' => $parentType], null, false); } - $page->addPageLayoutHandles(['type' => $type, 'id' => $category->getId()]); + $page->addPageLayoutHandles(['type' => $type], null, false); + $page->addPageLayoutHandles(['id' => $category->getId()]); // apply custom layout update once layout is loaded $layoutUpdates = $settings->getLayoutUpdates(); if ($layoutUpdates && is_array($layoutUpdates)) { foreach ($layoutUpdates as $layoutUpdate) { $page->addUpdate($layoutUpdate); - $page->addPageLayoutHandles(['layout_update' => md5($layoutUpdate)]); + $page->addPageLayoutHandles(['layout_update' => md5($layoutUpdate)], null, false); } } diff --git a/app/code/Magento/Catalog/Controller/Index/Index.php b/app/code/Magento/Catalog/Controller/Index/Index.php index 7512affdef3c9..4b83aee1a061e 100644 --- a/app/code/Magento/Catalog/Controller/Index/Index.php +++ b/app/code/Magento/Catalog/Controller/Index/Index.php @@ -1,6 +1,6 @@ resource = $resource; + $this->attributeRepository = $attributeRepository; + $this->scopeConfig = $scopeConfig; + } + + /** + * Delete all price values for non-admin stores if PRICE_SCOPE is global + * + * @return void + */ + public function execute() + { + $priceScope = $this->scopeConfig->getValue(Store::XML_PATH_PRICE_SCOPE); + if ($priceScope == Store::PRICE_SCOPE_GLOBAL) { + /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $priceAttribute */ + $priceAttribute = $this->attributeRepository + ->get(ProductAttributeInterface::ENTITY_TYPE_CODE, ProductAttributeInterface::CODE_PRICE); + $connection = $this->resource->getConnection(); + $conditions = [ + $connection->quoteInto('attribute_id = ?', $priceAttribute->getId()), + $connection->quoteInto('store_id != ?', Store::DEFAULT_STORE_ID), + ]; + + $connection->delete( + $priceAttribute->getBackend()->getTable(), + $conditions + ); + } + } +} diff --git a/app/code/Magento/Catalog/Cron/RefreshSpecialPrices.php b/app/code/Magento/Catalog/Cron/RefreshSpecialPrices.php index e3a63e0367328..5e04b026db3d4 100644 --- a/app/code/Magento/Catalog/Cron/RefreshSpecialPrices.php +++ b/app/code/Magento/Catalog/Cron/RefreshSpecialPrices.php @@ -1,6 +1,6 @@ _getModel()->setDestinationSubdir($this->getType()); - $this->_getModel()->setWidth($this->getWidth()); $this->_getModel()->setHeight($this->getHeight()); @@ -241,25 +240,25 @@ protected function setWatermarkProperties() { $this->setWatermark( $this->scopeConfig->getValue( - "design/watermark/{$this->_getModel()->getDestinationSubdir()}_image", + "design/watermark/{$this->getType()}_image", \Magento\Store\Model\ScopeInterface::SCOPE_STORE ) ); $this->setWatermarkImageOpacity( $this->scopeConfig->getValue( - "design/watermark/{$this->_getModel()->getDestinationSubdir()}_imageOpacity", + "design/watermark/{$this->getType()}_imageOpacity", \Magento\Store\Model\ScopeInterface::SCOPE_STORE ) ); $this->setWatermarkPosition( $this->scopeConfig->getValue( - "design/watermark/{$this->_getModel()->getDestinationSubdir()}_position", + "design/watermark/{$this->getType()}_position", \Magento\Store\Model\ScopeInterface::SCOPE_STORE ) ); $this->setWatermarkSize( $this->scopeConfig->getValue( - "design/watermark/{$this->_getModel()->getDestinationSubdir()}_size", + "design/watermark/{$this->getType()}_size", \Magento\Store\Model\ScopeInterface::SCOPE_STORE ) ); @@ -500,10 +499,7 @@ protected function initBaseFile() protected function isScheduledActionsAllowed() { $model = $this->_getModel(); - if ($model->isBaseFilePlaceholder() - && $model->getNewFile() === true - || $model->isCached() - ) { + if ($model->isBaseFilePlaceholder() || $model->isCached()) { return false; } return true; diff --git a/app/code/Magento/Catalog/Helper/Output.php b/app/code/Magento/Catalog/Helper/Output.php index 0b4b8a0dab9a6..a738b3fddfa07 100644 --- a/app/code/Magento/Catalog/Helper/Output.php +++ b/app/code/Magento/Catalog/Helper/Output.php @@ -1,6 +1,6 @@ getEncodedUrl($this->_getUrl('catalog/product_compare')); $data = [ - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $listCleanUrl, - 'product' => $product->getId() + \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => '', + 'product' => $product->getId(), + 'confirmation' => true, + 'confirmationMessage' => __('Are you sure you want to remove this item from your Compare Products list?') ]; return $this->postHelper->getPostData($this->getRemoveUrl(), $data); } @@ -253,9 +254,10 @@ public function getClearListUrl() */ public function getPostDataClearList() { - $refererUrl = $this->_getRequest()->getServer('HTTP_REFERER'); $params = [ - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $this->urlEncoder->encode($refererUrl) + \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => '', + 'confirmation' => true, + 'confirmationMessage' => __('Are you sure you want to remove all items from your Compare Products list?'), ]; return $this->postHelper->getPostData($this->getClearListUrl(), $params); } diff --git a/app/code/Magento/Catalog/Helper/Product/Composite.php b/app/code/Magento/Catalog/Helper/Product/Composite.php index fcd710ad1ec84..5e519540c78ca 100644 --- a/app/code/Magento/Catalog/Helper/Product/Composite.php +++ b/app/code/Magento/Catalog/Helper/Product/Composite.php @@ -1,6 +1,6 @@ _productOptionFactory = $productOptionFactory; $this->filter = $filter; $this->string = $string; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); parent::__construct($context); } @@ -105,7 +115,7 @@ public function getCustomOptions(\Magento\Catalog\Model\Product\Configuration\It $addOptions = $item->getOptionByCode('additional_options'); if ($addOptions) { - $options = array_merge($options, unserialize($addOptions->getValue())); + $options = array_merge($options, $this->serializer->unserialize($addOptions->getValue())); } return $options; diff --git a/app/code/Magento/Catalog/Helper/Product/Configuration/ConfigurationInterface.php b/app/code/Magento/Catalog/Helper/Product/Configuration/ConfigurationInterface.php index 0e47bc998e4bb..26b4e9e180327 100644 --- a/app/code/Magento/Catalog/Helper/Product/Configuration/ConfigurationInterface.php +++ b/app/code/Magento/Catalog/Helper/Product/Configuration/ConfigurationInterface.php @@ -1,6 +1,6 @@ getBeforeHandles()) { foreach ($params->getBeforeHandles() as $handle) { - $resultPage->addPageLayoutHandles( - ['id' => $product->getId(), 'sku' => $urlSafeSku, 'type' => $product->getTypeId()], - $handle - ); + $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle); + $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false); } } - $resultPage->addPageLayoutHandles( - ['id' => $product->getId(), 'sku' => $urlSafeSku, 'type' => $product->getTypeId()] - ); + $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku]); + $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], null, false); if ($params && $params->getAfterHandles()) { foreach ($params->getAfterHandles() as $handle) { - $resultPage->addPageLayoutHandles( - ['id' => $product->getId(), 'sku' => $urlSafeSku, 'type' => $product->getTypeId()], - $handle - ); + $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle); + $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false); } } diff --git a/app/code/Magento/Catalog/Model/AbstractModel.php b/app/code/Magento/Catalog/Model/AbstractModel.php index 40188c4efc9df..ccaa5829cd717 100644 --- a/app/code/Magento/Catalog/Model/AbstractModel.php +++ b/app/code/Magento/Catalog/Model/AbstractModel.php @@ -1,6 +1,6 @@ getAttribute()->getName(); $startDate = $object->getData($attributeName); - if ($startDate === false) { - return false; - } - if ($startDate == '' && $object->getSpecialPrice()) { - $startDate = $this->_localeDate->date(); - } return $startDate; } diff --git a/app/code/Magento/Catalog/Model/Attribute/Config.php b/app/code/Magento/Catalog/Model/Attribute/Config.php index 1b939c9118c35..4337bcb2a4707 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Config.php +++ b/app/code/Magento/Catalog/Model/Attribute/Config.php @@ -2,7 +2,7 @@ /** * High-level interface for catalog attributes data that hides format from the client code * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model\Attribute; diff --git a/app/code/Magento/Catalog/Model/Attribute/Config/Converter.php b/app/code/Magento/Catalog/Model/Attribute/Config/Converter.php index 0c9f79d78183b..04b1bf72c9280 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Config/Converter.php +++ b/app/code/Magento/Catalog/Model/Attribute/Config/Converter.php @@ -2,7 +2,7 @@ /** * Converter of attributes configuration from \DOMDocument to array * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model\Attribute\Config; diff --git a/app/code/Magento/Catalog/Model/Attribute/Config/Data.php b/app/code/Magento/Catalog/Model/Attribute/Config/Data.php index 032970a7461b6..831b7e211a92d 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Config/Data.php +++ b/app/code/Magento/Catalog/Model/Attribute/Config/Data.php @@ -1,22 +1,31 @@ getImage(); + $image = $this->getData($attributeCode); if ($image) { if (is_string($image)) { $url = $this->_storeManager->getStore()->getBaseUrl( diff --git a/app/code/Magento/Catalog/Model/Category/Attribute.php b/app/code/Magento/Catalog/Model/Category/Attribute.php index 51c3b87937cf7..94a108c063f51 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute.php @@ -1,6 +1,6 @@ getAttribute()->getName(); + $value = $object->getData($attributeName); + + if ($imageName = $this->getUploadedImageName($value)) { + $object->setData($this->additionalData . $attributeName, $value); + $object->setData($attributeName, $imageName); + } else if (!is_string($value)) { + $object->setData($attributeName, ''); + } + + return parent::beforeSave($object); + } + + /** * @return \Magento\Catalog\Model\ImageUploader * * @deprecated @@ -79,10 +112,10 @@ public function __construct( private function getImageUploader() { if ($this->imageUploader === null) { - $this->imageUploader = \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Catalog\CategoryImageUpload::class - ); + $this->imageUploader = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Catalog\CategoryImageUpload::class); } + return $this->imageUploader; } @@ -94,15 +127,16 @@ private function getImageUploader() */ public function afterSave($object) { - $image = $object->getData($this->getAttribute()->getName(), null); + $value = $object->getData($this->additionalData . $this->getAttribute()->getName()); - if ($image !== null) { + if ($imageName = $this->getUploadedImageName($value)) { try { - $this->getImageUploader()->moveFileFromTmp($image); + $this->getImageUploader()->moveFileFromTmp($imageName); } catch (\Exception $e) { $this->_logger->critical($e); } } + return $this; } } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php index 516ce756dff97..d18146ebae85e 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php @@ -1,6 +1,6 @@ storeManager = $storeManager; $this->request = $request; $this->categoryFactory = $categoryFactory; + parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); - $this->meta = $this->prepareMeta($this->meta); + } + + /** + * @inheritdoc + */ + public function getMeta() + { + $meta = parent::getMeta(); + $meta = $this->prepareMeta($meta); + + $category = $this->getCurrentCategory(); + + if ($category) { + $meta = $this->addUseDefaultValueCheckbox($category, $meta); + $meta = $this->resolveParentInheritance($category, $meta); + } + + return $meta; + } + + /** + * @param Category $category + * @param array $meta + * @return array + */ + private function addUseDefaultValueCheckbox(Category $category, array $meta) + { + /** @var EavAttributeInterface $attribute */ + foreach ($category->getAttributes() as $attribute) { + $attributeCode = $attribute->getAttributeCode(); + $canDisplayUseDefault = $attribute->getScope() != EavAttributeInterface::SCOPE_GLOBAL_TEXT + && $category->getId() + && $category->getStoreId(); + $attributePath = $this->getArrayManager()->findPath($attributeCode, $meta); + + if ( + !$attributePath + || !$canDisplayUseDefault + || in_array($attributeCode, $this->elementsWithUseConfigSetting) + ) { + continue; + } + + $meta = $this->getArrayManager()->merge( + [$attributePath, 'arguments/data/config'], + $meta, + [ + 'service' => [ + 'template' => 'ui/form/element/helper/service', + ], + 'disabled' => !$this->getScopeOverriddenValue()->containsValue( + CategoryInterface::class, + $category, + $attributeCode, + $this->request->getParam($this->requestScopeFieldName, Store::DEFAULT_STORE_ID) + ) + ] + ); + } + + return $meta; + } + + /** + * Removes not necessary inheritance fields + * + * @param Category $category + * @param array $meta + * @return array + */ + private function resolveParentInheritance(Category $category, array $meta) + { + if (!$category->getParentId() || !$this->getArrayManager()->findPath('custom_use_parent_settings', $meta)) { + return $meta; + } + + $meta = $this->getArrayManager()->merge( + [$this->getArrayManager()->findPath('custom_use_parent_settings', $meta), 'arguments/data/config'], + $meta, + ['visible' => false] + ); + + return $meta; } /** @@ -203,14 +308,10 @@ public function getData() $category = $this->getCurrentCategory(); if ($category) { $categoryData = $category->getData(); - $categoryData = $this->addUseDefaultSettings($category, $categoryData); $categoryData = $this->addUseConfigSettings($categoryData); $categoryData = $this->filterFields($categoryData); - if (isset($categoryData['image'])) { - unset($categoryData['image']); - $categoryData['image'][0]['name'] = $category->getData('image'); - $categoryData['image'][0]['url'] = $category->getImageUrl(); - } + $categoryData = $this->convertValues($category, $categoryData); + $this->loadedData[$category->getId()] = $categoryData; } return $this->loadedData; @@ -294,6 +395,7 @@ protected function addUseConfigSettings($categoryData) * @param \Magento\Catalog\Model\Category $category * @param array $categoryData * @return array + * @deprecated */ protected function addUseDefaultSettings($category, $categoryData) { @@ -371,6 +473,39 @@ protected function filterFields($categoryData) return array_diff_key($categoryData, array_flip($this->ignoreFields)); } + /** + * Converts category image data to acceptable for rendering format + * + * @param \Magento\Catalog\Model\Category $category + * @param array $categoryData + * @return array + */ + private function convertValues($category, $categoryData) + { + foreach ($category->getAttributes() as $attributeCode => $attribute) { + if (!isset($categoryData[$attributeCode])) { + continue; + } + + if ($attribute->getBackend() instanceof ImageBackendModel) { + unset($categoryData[$attributeCode]); + + $fileName = $category->getData($attributeCode); + if ($this->getFileInfo()->isExist($fileName)) { + $stat = $this->getFileInfo()->getStat($fileName); + $mime = $this->getFileInfo()->getMimeType($fileName); + + $categoryData[$attributeCode][0]['name'] = $fileName; + $categoryData[$attributeCode][0]['url'] = $category->getImageUrl($attributeCode); + $categoryData['image'][0]['size'] = isset($stat) ? $stat['size'] : 0; + $categoryData['image'][0]['type'] = $mime; + } + } + } + + return $categoryData; + } + /** * Category's fields default values * @@ -383,15 +518,6 @@ public function getDefaultMetaData($result) $result['use_config.available_sort_by']['default'] = true; $result['use_config.default_sort_by']['default'] = true; $result['use_config.filter_price_range']['default'] = true; - if ($this->request->getParam('store') && $this->request->getParam('id')) { - $result['use_default.url_key']['checked'] = true; - $result['use_default.url_key']['default'] = true; - $result['use_default.url_key']['visible'] = true; - } else { - $result['use_default.url_key']['checked'] = false; - $result['use_default.url_key']['default'] = false; - $result['use_default.url_key']['visible'] = false; - } return $result; } @@ -431,7 +557,6 @@ protected function getFieldsMap() [ 'url_key', 'url_key_create_redirect', - 'use_default.url_key', 'url_key_group', 'meta_title', 'meta_keywords', @@ -461,4 +586,53 @@ protected function getFieldsMap() ], ]; } + + /** + * Retrieve scope overridden value + * + * @return ScopeOverriddenValue + * @deprecated + */ + private function getScopeOverriddenValue() + { + if (null === $this->scopeOverriddenValue) { + $this->scopeOverriddenValue = \Magento\Framework\App\ObjectManager::getInstance()->get( + ScopeOverriddenValue::class + ); + } + + return $this->scopeOverriddenValue; + } + + /** + * Retrieve array manager + * + * @return ArrayManager + * @deprecated + */ + private function getArrayManager() + { + if (null === $this->arrayManager) { + $this->arrayManager = \Magento\Framework\App\ObjectManager::getInstance()->get( + ArrayManager::class + ); + } + + return $this->arrayManager; + } + + /** + * Get FileInfo instance + * + * @return FileInfo + * + * @deprecated + */ + private function getFileInfo() + { + if ($this->fileInfo === null) { + $this->fileInfo = ObjectManager::getInstance()->get(FileInfo::class); + } + return $this->fileInfo; + } } diff --git a/app/code/Magento/Catalog/Model/Category/FileInfo.php b/app/code/Magento/Catalog/Model/Category/FileInfo.php new file mode 100644 index 0000000000000..9a4c1f9f243b3 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Category/FileInfo.php @@ -0,0 +1,107 @@ +filesystem = $filesystem; + $this->mime = $mime; + } + + /** + * Get WriteInterface instance + * + * @return WriteInterface + */ + private function getMediaDirectory() + { + if ($this->mediaDirectory === null) { + $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + } + return $this->mediaDirectory; + } + + /** + * Retrieve MIME type of requested file + * + * @param string $fileName + * @return string + */ + public function getMimeType($fileName) + { + $filePath = self::ENTITY_MEDIA_PATH . '/' . ltrim($fileName, '/'); + $absoluteFilePath = $this->getMediaDirectory()->getAbsolutePath($filePath); + + $result = $this->mime->getMimeType($absoluteFilePath); + return $result; + } + + /** + * Get file statistics data + * + * @param string $fileName + * @return array + */ + public function getStat($fileName) + { + $filePath = self::ENTITY_MEDIA_PATH . '/' . ltrim($fileName, '/'); + + $result = $this->getMediaDirectory()->stat($filePath); + return $result; + } + + /** + * Check if the file exists + * + * @param string $fileName + * @return bool + */ + public function isExist($fileName) + { + $filePath = self::ENTITY_MEDIA_PATH . '/' . ltrim($fileName, '/'); + + $result = $this->getMediaDirectory()->isExist($filePath); + return $result; + } +} diff --git a/app/code/Magento/Catalog/Model/Category/Link/ReadHandler.php b/app/code/Magento/Catalog/Model/Category/Link/ReadHandler.php index 6ac347cc1e4a2..4a76f0aea1595 100644 --- a/app/code/Magento/Catalog/Model/Category/Link/ReadHandler.php +++ b/app/code/Magento/Catalog/Model/Category/Link/ReadHandler.php @@ -1,6 +1,6 @@ _scopeConfig = $scopeConfig; $this->_configFactory = $configFactory; @@ -157,7 +160,14 @@ public function __construct( $this->_storeManager = $storeManager; $this->_eavConfig = $eavConfig; - parent::__construct($cache, $entityTypeFactory, $entityTypeCollectionFactory, $cacheState, $universalFactory); + parent::__construct( + $cache, + $entityTypeFactory, + $entityTypeCollectionFactory, + $cacheState, + $universalFactory, + $serializer + ); } /** diff --git a/app/code/Magento/Catalog/Model/Config/Backend/Category.php b/app/code/Magento/Catalog/Model/Config/Backend/Category.php index 4bb345b33d38e..2e1fcc2c42ac2 100644 --- a/app/code/Magento/Catalog/Model/Config/Backend/Category.php +++ b/app/code/Magento/Catalog/Model/Config/Backend/Category.php @@ -1,6 +1,6 @@ self::VALUE_FIXED, 'label' => __('Fixed')], + ['value' => self::VALUE_PERCENT, 'label' => __('Discount')], + ]; + } +} diff --git a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Type.php b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Type.php index 15c0941655d3b..423bbe5ca5781 100644 --- a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Type.php +++ b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Type.php @@ -1,6 +1,6 @@ objectFactory = $objectFactory; $this->productOptionFactory = $productOptionFactory; $this->extensionFactory = $extensionFactory; $this->customOptionFactory = $customOptionFactory; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** @@ -99,7 +110,7 @@ public function processOptions(CartItemInterface $cartItem) protected function getOptions(CartItemInterface $cartItem) { $buyRequest = !empty($cartItem->getOptionByCode('info_buyRequest')) - ? unserialize($cartItem->getOptionByCode('info_buyRequest')->getValue()) + ? $this->serializer->unserialize($cartItem->getOptionByCode('info_buyRequest')->getValue()) : null; return is_array($buyRequest) && isset($buyRequest['options']) ? $buyRequest['options'] diff --git a/app/code/Magento/Catalog/Model/Design.php b/app/code/Magento/Catalog/Model/Design.php index ad5cc4ff42fe0..136f36a2a1398 100644 --- a/app/code/Magento/Catalog/Model/Design.php +++ b/app/code/Magento/Catalog/Model/Design.php @@ -1,6 +1,6 @@ resource = $resource; $this->connection = $resource->getConnection(); $this->storeManager = $storeManager; $this->config = $config; + $this->queryGenerator = $queryGenerator ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(QueryGenerator::class); } /** @@ -309,15 +320,26 @@ protected function isRangingNeeded() * @param int $range * @return \Magento\Framework\DB\Select[] */ - protected function prepareSelectsByRange(\Magento\Framework\DB\Select $select, $field, $range = self::RANGE_CATEGORY_STEP) - { - return $this->isRangingNeeded() ? $this->connection->selectsByRange( - $field, - $select, - $range - ) : [ - $select - ]; + protected function prepareSelectsByRange( + \Magento\Framework\DB\Select $select, + $field, + $range = self::RANGE_CATEGORY_STEP + ) { + if($this->isRangingNeeded()) { + $iterator = $this->queryGenerator->generate( + $field, + $select, + $range, + \Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR + ); + + $queries = []; + foreach ($iterator as $query) { + $queries[] = $query; + } + return $queries; + } + return [$select]; } /** diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php index ba093c0129855..0f361ec64b1e0 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php @@ -1,6 +1,6 @@ _productIndexerHelper->getAttribute('status'); @@ -263,7 +267,7 @@ protected function _fillTemporaryFlatTable(array $tables, $storeId, $valueFieldS $select->joinLeft( $temporaryTableName, - "e.entity_id = " . $temporaryTableName . ".entity_id", + sprintf('e.%1$s = %2$s.%1$s', $linkField, $temporaryTableName), $columnsNames ); $allColumns = array_merge($allColumns, $columnsNames); @@ -277,7 +281,7 @@ protected function _fillTemporaryFlatTable(array $tables, $storeId, $valueFieldS if (!empty($columnValueNames)) { $select->joinLeft( $temporaryValueTableName, - "e.${linkField} = " . $temporaryValueTableName . ".entity_id", + sprintf('e.%1$s = %2$s.%1$s', $linkField, $temporaryValueTableName), $columnValueNames ); $allColumns = array_merge($allColumns, $columnValueNames); diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Plugin/IndexerConfigData.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Plugin/IndexerConfigData.php index 0777e9a06e348..34bcfa2e484e5 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Plugin/IndexerConfigData.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Plugin/IndexerConfigData.php @@ -1,6 +1,6 @@ tableInstance = $connection->newTable($tableName); + } + + /** + * @inheritdoc + */ + public function addColumn($name, $type, $size = null, $options = [], $comment = null) + { + $this->tableInstance->addColumn($name, $type, $size, $options, $comment); + return $this; + } + + /** + * @inheritdoc + */ + public function getTable() + { + return $this->tableInstance; + } +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/BuilderInterface.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/BuilderInterface.php new file mode 100644 index 0000000000000..3651821ae10ac --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/BuilderInterface.php @@ -0,0 +1,40 @@ +_connection->newTable($tableName); - $valueTemporaryTable = $this->_connection->newTable($valueTableName); + $temporaryTableBuilder = $this->getTableBuilderFactory()->create( + [ + 'connection' => $this->_connection, + 'tableName' => $tableName + ] + ); + $valueTemporaryTableBuilder = $this->getTableBuilderFactory()->create( + [ + 'connection' => $this->_connection, + 'tableName' => $valueTableName + ] + ); $flatColumns = $this->_productIndexerHelper->getFlatColumns(); - $temporaryTable->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); + $temporaryTableBuilder->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); - $temporaryTable->addColumn('type_id', \Magento\Framework\DB\Ddl\Table::TYPE_TEXT); + $temporaryTableBuilder->addColumn('type_id', \Magento\Framework\DB\Ddl\Table::TYPE_TEXT); - $temporaryTable->addColumn('attribute_set_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); + $temporaryTableBuilder->addColumn('attribute_set_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); - $valueTemporaryTable->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); + $valueTemporaryTableBuilder->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ foreach ($columns as $columnName => $attribute) { @@ -145,7 +160,7 @@ protected function _createTemporaryTable($tableName, array $columns, $valueField $column = $column[$attributeCode]; } - $temporaryTable->addColumn( + $temporaryTableBuilder->addColumn( $columnName, $column['type'], isset($column['length']) ? $column['length'] : null @@ -154,7 +169,7 @@ protected function _createTemporaryTable($tableName, array $columns, $valueField $columnValueName = $attributeCode . $valueFieldSuffix; if (isset($flatColumns[$columnValueName])) { $columnValue = $flatColumns[$columnValueName]; - $valueTemporaryTable->addColumn( + $valueTemporaryTableBuilder->addColumn( $columnValueName, $columnValue['type'], isset($columnValue['length']) ? $columnValue['length'] : null @@ -162,11 +177,11 @@ protected function _createTemporaryTable($tableName, array $columns, $valueField } } $this->_connection->dropTemporaryTable($tableName); - $this->_connection->createTemporaryTable($temporaryTable); + $this->_connection->createTemporaryTable($temporaryTableBuilder->getTable()); - if (count($valueTemporaryTable->getColumns()) > 1) { + if (count($valueTemporaryTableBuilder->getTable()->getColumns()) > 1) { $this->_connection->dropTemporaryTable($valueTableName); - $this->_connection->createTemporaryTable($valueTemporaryTable); + $this->_connection->createTemporaryTable($valueTemporaryTableBuilder->getTable()); $valueTables[$valueTableName] = $valueTableName; } } @@ -197,7 +212,8 @@ protected function _fillTemporaryEntityTable($tableName, array $columns, array $ if (!empty($columns)) { $select = $this->_connection->select(); $temporaryEntityTable = $this->_getTemporaryTableName($tableName); - $idsColumns = ['entity_id', 'type_id', 'attribute_set_id']; + $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + $idsColumns = array_unique([$metadata->getLinkField(), 'entity_id', 'type_id', 'attribute_set_id']); $columns = array_merge($idsColumns, array_keys($columns)); @@ -261,7 +277,7 @@ protected function _fillTemporaryTable( ); $temporaryTableName = $this->_getTemporaryTableName($tableName); $temporaryValueTableName = $temporaryTableName . $valueFieldSuffix; - $keyColumn = ['entity_id']; + $keyColumn = array_unique([$metadata->getLinkField(), 'entity_id']); $columns = array_merge($keyColumn, array_keys($columnsList)); $valueColumns = $keyColumn; $flatColumns = $this->_productIndexerHelper->getFlatColumns(); @@ -333,6 +349,19 @@ protected function _fillTemporaryTable( } } + /** + * @return BuilderInterfaceFactory + */ + private function getTableBuilderFactory() + { + if (null === $this->tableBuilderFactory) { + $this->tableBuilderFactory = \Magento\Framework\App\ObjectManager::getInstance() + ->get(BuilderInterfaceFactory::class); + } + + return $this->tableBuilderFactory; + } + /** * @return \Magento\Framework\EntityManager\MetadataPool */ diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableData.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableData.php index a5a1b7d808f58..8f12fbfd358dc 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableData.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableData.php @@ -1,6 +1,6 @@ hasData('salable') && !$this->_catalogProduct->getSkipSaleableCheck()) { + return $this->getData('salable'); + } $this->_eventManager->dispatch('catalog_product_is_salable_before', ['product' => $this]); $salable = $this->isAvailable(); @@ -1626,6 +1628,7 @@ public function isSalable() 'catalog_product_is_salable_after', ['product' => $this, 'salable' => $object] ); + $this->setData('salable', $object->getIsSalable()); return $object->getIsSalable(); } @@ -2614,4 +2617,16 @@ private function getMediaGalleryProcessor() } return $this->mediaGalleryProcessor; } + + /** + * Set the associated products + * + * @param array $productIds + * @return $this + */ + public function setAssociatedProductIds(array $productIds) + { + $this->getExtensionAttributes()->setConfigurableProductLinks($productIds); + return $this; + } } diff --git a/app/code/Magento/Catalog/Model/Product/Action.php b/app/code/Magento/Catalog/Model/Product/Action.php index 45194cbee0261..84502202981f7 100644 --- a/app/code/Magento/Catalog/Model/Product/Action.php +++ b/app/code/Magento/Catalog/Model/Product/Action.php @@ -1,6 +1,6 @@ _catalogProductType = $catalogProductType; $this->_groupManagement = $groupManagement; - parent::__construct($currencyFactory, $storeManager, $catalogData, $config, $localeFormat); + parent::__construct( + $currencyFactory, + $storeManager, + $catalogData, + $config, + $localeFormat, + $scopeOverriddenValue + ); } /** diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Media/EntryConverterInterface.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Media/EntryConverterInterface.php index 450961d8f6289..3b9f46cfa143f 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Media/EntryConverterInterface.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Media/EntryConverterInterface.php @@ -1,6 +1,6 @@ */ @@ -48,26 +49,33 @@ class Price extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend protected $localeFormat; /** - * Construct - * + * @var \Magento\Catalog\Model\Attribute\ScopeOverriddenValue + */ + private $scopeOverriddenValue; + + /** * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Helper\Data $catalogData * @param \Magento\Framework\App\Config\ScopeConfigInterface $config * @param \Magento\Framework\Locale\FormatInterface $localeFormat + * @param ScopeOverriddenValue|null $scopeOverriddenValue */ public function __construct( \Magento\Directory\Model\CurrencyFactory $currencyFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Helper\Data $catalogData, \Magento\Framework\App\Config\ScopeConfigInterface $config, - \Magento\Framework\Locale\FormatInterface $localeFormat + \Magento\Framework\Locale\FormatInterface $localeFormat, + ScopeOverriddenValue $scopeOverriddenValue = null ) { $this->_currencyFactory = $currencyFactory; $this->_storeManager = $storeManager; $this->_helper = $catalogData; $this->_config = $config; $this->localeFormat = $localeFormat; + $this->scopeOverriddenValue = $scopeOverriddenValue + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ScopeOverriddenValue::class); } /** @@ -102,43 +110,29 @@ public function setScope($attribute) } /** - * After Save Attribute manipulation + * After Save Price Attribute manipulation + * Processes product price attributes if price scoped to website and updates data when: + * * Price changed for non-default store view - will update price for all stores assigned to current website. + * * Price will be changed according to store currency even if price changed in product with default store id. + * * In a case when price was removed for non-default store (use default option checked) the default store price + * * will be used instead * * @param \Magento\Catalog\Model\Product $object * @return $this - * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function afterSave($object) { - $value = $object->getData($this->getAttribute()->getAttributeCode()); - /** - * Orig value is only for existing objects - */ - $oridData = $object->getOrigData(); - $origValueExist = $oridData && array_key_exists($this->getAttribute()->getAttributeCode(), $oridData); - if ($object->getStoreId() != 0 || !$value || $origValueExist) { - return $this; - } - - if ($this->getAttribute()->getIsGlobal() == ScopedAttributeInterface::SCOPE_WEBSITE) { - $baseCurrency = $this->_config->getValue( - \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, - 'default' - ); - - $storeIds = $object->getStoreIds(); - if (is_array($storeIds)) { - foreach ($storeIds as $storeId) { - $storeCurrency = $this->_storeManager->getStore($storeId)->getBaseCurrencyCode(); - if ($storeCurrency == $baseCurrency) { - continue; - } - $rate = $this->_currencyFactory->create()->load($baseCurrency)->getRate($storeCurrency); - if (!$rate) { - $rate = 1; - } - $newValue = $value * $rate; - $object->addAttributeUpdate($this->getAttribute()->getAttributeCode(), $newValue, $storeId); + /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ + $attribute = $this->getAttribute(); + $attributeCode = $attribute->getAttributeCode(); + $value = $object->getData($attributeCode); + if ((float)$value > 0) { + if ($attribute->isScopeWebsite() && $object->getStoreId() != \Magento\Store\Model\Store::DEFAULT_STORE_ID) { + if ($this->isUseDefault($object)) { + $value = null; + } + foreach ((array)$object->getWebsiteStoreIds() as $storeId) { + $object->addAttributeUpdate($attributeCode, $value, $storeId); } } } @@ -146,6 +140,22 @@ public function afterSave($object) return $this; } + /** + * Check whether product uses default attribute's value in selected scope + * @param \Magento\Catalog\Model\Product $object + * @return bool + */ + private function isUseDefault($object) + { + $overridden = $this->scopeOverriddenValue->containsValue( + \Magento\Catalog\Api\Data\ProductInterface::class, + $object, + $this->getAttribute()->getAttributeCode(), + $object->getStoreId() + ); + return !$overridden; + } + /** * Validate * diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php index 327729cf83695..be98147abfc85 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php @@ -1,6 +1,6 @@ getAttribute(); $entity = $attribute->getEntity(); - $increment = $this->_getLastSimilarAttributeValueIncrement($attribute, $object); $attributeValue = $object->getData($attribute->getAttributeCode()); + $increment = null; while (!$entity->checkAttributeUniqueValue($attribute, $object)) { + if ($increment === null) { + $increment = $this->_getLastSimilarAttributeValueIncrement($attribute, $object); + } $sku = trim($attributeValue); if (strlen($sku . '-' . ++$increment) > self::SKU_MAX_LENGTH) { $sku = substr($sku, 0, -strlen($increment) - 1); diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Stock.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Stock.php index 5c3fd4730aaed..b1b81512b53c4 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Stock.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Stock.php @@ -1,6 +1,6 @@ getStockData() !== null || $stockData !== null) { + if ($object->getStockData() !== null && $stockData !== null) { $object->setStockData(array_replace((array)$object->getStockData(), (array)$stockData)); } $object->unsetData($this->getAttribute()->getAttributeCode()); diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php index 480f8e8942e87..0153d2bfc5741 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php @@ -1,6 +1,6 @@ _productAttributeBackendTierprice = $productAttributeTierprice; parent::__construct( @@ -48,7 +52,8 @@ public function __construct( $config, $localeFormat, $catalogProductType, - $groupManagement + $groupManagement, + $scopeOverriddenValue ); } @@ -157,6 +162,7 @@ protected function modifyPriceData($object, $data) $data = parent::modifyPriceData($object, $data); foreach ($data as $key => $tierPrice) { if ($this->getPercentage($tierPrice)) { + $data[$key]['price'] = $object->getPrice() * (1 - $this->getPercentage($tierPrice) / 100); $data[$key]['website_price'] = $object->getPrice() * (1 - $this->getPercentage($tierPrice) / 100); } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Weight.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Weight.php index 3dfb0040fe6ac..495c62b429365 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Weight.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Weight.php @@ -1,6 +1,6 @@ - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model\Product\Attribute; diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Management.php b/app/code/Magento/Catalog/Model/Product/Attribute/Management.php index fa9b2ddee3cfe..4529b61147402 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Management.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Management.php @@ -1,7 +1,7 @@ - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model\Product\Attribute; diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php index e72d5b48a3691..b7d592263163a 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php @@ -1,7 +1,7 @@ setFrontendInput($existingModel->getFrontendInput()); if (is_array($attribute->getFrontendLabels())) { - $frontendLabel[0] = $existingModel->getDefaultFrontendLabel(); + $defaultFrontendLabel = $attribute->getDefaultFrontendLabel(); + $frontendLabel[0] = !empty($defaultFrontendLabel) + ? $defaultFrontendLabel + : $existingModel->getDefaultFrontendLabel(); foreach ($attribute->getFrontendLabels() as $item) { $frontendLabel[$item->getStoreId()] = $item->getLabel(); } $attribute->setDefaultFrontendLabel($frontendLabel); } - if (!$attribute->getIsUserDefined()) { - // Unset attribute field for system attributes - $attribute->setApplyTo(null); - } } else { $attribute->setAttributeId(null); @@ -178,13 +172,31 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib ); $attribute->setIsUserDefined(1); } - $this->attributeResource->save($attribute); - if (!empty($attribute->getData(AttributeInterface::OPTIONS))) { + $options = []; + $sortOrder = 0; + $default = []; + $optionIndex = 0; foreach ($attribute->getOptions() as $option) { - $this->getOptionManagement()->add($attribute->getAttributeCode(), $option); + $optionIndex++; + $optionId = $option->getValue() ?: 'option_' . $optionIndex; + $options['value'][$optionId][0] = $option->getLabel(); + $options['order'][$optionId] = $option->getSortOrder() ?: $sortOrder++; + if (is_array($option->getStoreLabels())) { + foreach ($option->getStoreLabels() as $label) { + $options['value'][$optionId][$label->getStoreId()] = $label->getLabel(); + } + } + if ($option->getIsDefault()) { + $default[] = $optionId; + } + } + $attribute->setDefault($default); + if (count($options)) { + $attribute->setOption($options); } } + $this->attributeResource->save($attribute); return $this->get($attribute->getAttributeCode()); } @@ -263,16 +275,4 @@ protected function validateFrontendInput($frontendInput) throw InputException::invalidFieldValue('frontend_input', $frontendInput); } } - - /** - * @return \Magento\Catalog\Api\ProductAttributeOptionManagementInterface - */ - private function getOptionManagement() - { - if (null === $this->optionManagement) { - $this->optionManagement = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Api\ProductAttributeOptionManagementInterface::class); - } - return $this->optionManagement; - } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/SetManagement.php b/app/code/Magento/Catalog/Model/Product/Attribute/SetManagement.php index d69e5a64939e9..e5ef94968d441 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/SetManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/SetManagement.php @@ -1,7 +1,7 @@ _storeManager->getStore()->getCode(); if ($cache = $this->_configCacheType->load($cacheKey)) { - $options = unserialize($cache); + $options = $this->getSerializer()->unserialize($cache); } else { /** @var \Magento\Directory\Model\Country $country */ $country = $this->_countryFactory->create(); /** @var \Magento\Directory\Model\ResourceModel\Country\Collection $collection */ $collection = $country->getResourceCollection(); $options = $collection->load()->toOptionArray(); - $this->_configCacheType->save(serialize($options), $cacheKey); + $this->_configCacheType->save($this->getSerializer()->serialize($options), $cacheKey); } return $options; } + + /** + * Get serializer + * + * @return \Magento\Framework\Serialize\SerializerInterface + * @deprecated + */ + private function getSerializer() + { + if ($this->serializer === null) { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\SerializerInterface::class); + } + return $this->serializer; + } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/Inputtype.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/Inputtype.php index f43d106248ede..be839be021e84 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Source/Inputtype.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/Inputtype.php @@ -1,6 +1,6 @@ mediaConfig->getMediaAttributeCodes() as $mediaAttrCode) { + foreach ($this->getMediaAttributeCodes() as $mediaAttrCode) { $attrData = $product->getData($mediaAttrCode); - + if (empty($attrData) && empty($clearImages) && empty($newImages) && empty($existImages)) { + continue; + } if (in_array($attrData, $clearImages)) { $product->setData($mediaAttrCode, 'no_selection'); } @@ -160,12 +167,13 @@ public function execute($product, $arguments = []) if (in_array($attrData, array_keys($existImages))) { $product->setData($mediaAttrCode . '_label', $existImages[$attrData]['label']); } - - $product->addAttributeUpdate( - $mediaAttrCode, - $product->getData($mediaAttrCode), - $product->getStoreId() - ); + if (!empty($product->getData($mediaAttrCode))) { + $product->addAttributeUpdate( + $mediaAttrCode, + $product->getData($mediaAttrCode), + $product->getStoreId() + ); + } } $product->setData($attrCode, $value); @@ -393,4 +401,17 @@ protected function copyImage($file) ); } } + + /** + * Get Media Attribute Codes cached value + * + * @return array + */ + private function getMediaAttributeCodes() + { + if ($this->mediaAttributeCodes === null) { + $this->mediaAttributeCodes = $this->mediaConfig->getMediaAttributeCodes(); + } + return $this->mediaAttributeCodes; + } } diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Entry.php b/app/code/Magento/Catalog/Model/Product/Gallery/Entry.php index cbe876561ad2e..8cfed2631994e 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/Entry.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/Entry.php @@ -1,7 +1,7 @@ $existingEntry) { + $entryTypes = (array)$entry->getTypes(); + $existingEntryTypes = (array)$existingMediaGalleryEntries[$key]->getTypes(); + $existingMediaGalleryEntries[$key]->setTypes(array_diff($existingEntryTypes, $entryTypes)); + if ($existingEntry->getId() == $entry->getId()) { $found = true; if ($entry->getFile()) { $entry->setId(null); } $existingMediaGalleryEntries[$key] = $entry; - break; } } if (!$found) { diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/MimeTypeExtensionMap.php b/app/code/Magento/Catalog/Model/Product/Gallery/MimeTypeExtensionMap.php index e116e1b4f1cc8..4242539b9fe8b 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/MimeTypeExtensionMap.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/MimeTypeExtensionMap.php @@ -1,7 +1,7 @@ setData($attribute, null); + $product->setData($attribute, 'no_selection'); } } } elseif (in_array($mediaAttribute, $mediaAttributeCodes)) { - $product->setData($mediaAttribute, null); + $product->setData($mediaAttribute, 'no_selection'); } return $this; diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php index 28ecfff39c94f..1ed2ef20360e9 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php @@ -1,6 +1,6 @@ resourceModel->countImageUses($image['file']) > 1) { + $filesToDelete[] = ltrim($image['file'], '/'); + } } } } diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index 7d8b464db3b34..03a3cdd044043 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -1,6 +1,6 @@ _coreFileStorageDatabase = $coreFileStorageDatabase; parent::__construct($context, $registry, $resource, $resourceCollection, $data); $this->_mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); - $result = $this->_mediaDirectory->create($this->_catalogProductMediaConfig->getBaseMediaPath()); $this->_imageFactory = $imageFactory; $this->_assetRepo = $assetRepo; $this->_viewFileSystem = $viewFileSystem; @@ -450,86 +464,29 @@ protected function _rgbToString($rgbArray) * @param string $file * @return $this * @throws \Exception - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) */ public function setBaseFile($file) { $this->_isBaseFilePlaceholder = false; - if ($file && 0 !== strpos($file, '/', 0)) { - $file = '/' . $file; - } - $baseDir = $this->_catalogProductMediaConfig->getBaseMediaPath(); - - if ('/no_selection' == $file) { - $file = null; - } - if ($file) { - if (!$this->_fileExists($baseDir . $file) || !$this->_checkMemory($baseDir . $file)) { - $file = null; - } - } - if (!$file) { + $this->imageAsset = $this->getViewAssetImageFactory()->create( + [ + 'miscParams' => $this->getMiscParams(), + 'filePath' => $file, + ] + ); + if ($file == 'no_selection' || !$this->_fileExists($this->imageAsset->getSourceFile()) + || !$this->_checkMemory($this->imageAsset->getSourceFile()) + ) { $this->_isBaseFilePlaceholder = true; - // check if placeholder defined in config - $isConfigPlaceholder = $this->_scopeConfig->getValue( - "catalog/placeholder/{$this->getDestinationSubdir()}_placeholder", - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + $this->imageAsset = $this->getViewAssetPlaceholderFactory()->create( + [ + 'type' => $this->getDestinationSubdir(), + ] ); - $configPlaceholder = '/placeholder/' . $isConfigPlaceholder; - if (!empty($isConfigPlaceholder) && $this->_fileExists($baseDir . $configPlaceholder)) { - $file = $configPlaceholder; - } else { - $this->_newFile = true; - return $this; - } - } - - $baseFile = $baseDir . $file; - - if (!$file || !$this->_mediaDirectory->isFile($baseFile)) { - throw new \Exception(__('We can\'t find the image file.')); } - $this->_baseFile = $baseFile; - - // build new filename (most important params) - $path = [ - $this->_catalogProductMediaConfig->getBaseMediaPath(), - 'cache', - $this->_storeManager->getStore()->getId(), - $path[] = $this->getDestinationSubdir(), - ]; - if (!empty($this->_width) || !empty($this->_height)) { - $path[] = "{$this->_width}x{$this->_height}"; - } - - // add misk params as a hash - $miscParams = [ - ($this->_keepAspectRatio ? '' : 'non') . 'proportional', - ($this->_keepFrame ? '' : 'no') . 'frame', - ($this->_keepTransparency ? '' : 'no') . 'transparency', - ($this->_constrainOnly ? 'do' : 'not') . 'constrainonly', - $this->_rgbToString($this->_backgroundColor), - 'angle' . $this->_angle, - 'quality' . $this->_quality, - ]; - - // if has watermark add watermark params to hash - if ($this->getWatermarkFile()) { - $miscParams[] = $this->getWatermarkFile(); - $miscParams[] = $this->getWatermarkImageOpacity(); - $miscParams[] = $this->getWatermarkPosition(); - $miscParams[] = $this->getWatermarkWidth(); - $miscParams[] = $this->getWatermarkHeight(); - } - - $path[] = md5(implode('_', $miscParams)); - - // append prepared filename - $this->_newFile = implode('/', $path) . $file; - // the $file contains heading slash + $this->_baseFile = $this->imageAsset->getSourceFile(); return $this; } @@ -543,6 +500,7 @@ public function getBaseFile() } /** + * @deprecated * @return bool|string */ public function getNewFile() @@ -691,10 +649,10 @@ public function setWatermark( */ public function saveFile() { - if ($this->_isBaseFilePlaceholder && $this->_newFile === true) { + if ($this->_isBaseFilePlaceholder) { return $this; } - $filename = $this->_mediaDirectory->getAbsolutePath($this->getNewFile()); + $filename = $this->getBaseFile() ? $this->imageAsset->getPath() : null; $this->getImageProcessor()->save($filename); $this->_coreFileStorageDatabase->saveFile($filename); return $this; @@ -705,17 +663,7 @@ public function saveFile() */ public function getUrl() { - if ($this->_newFile === true) { - $url = $this->_assetRepo->getUrl( - "Magento_Catalog::images/product/placeholder/{$this->getDestinationSubdir()}.jpg" - ); - } else { - $url = $this->_storeManager->getStore()->getBaseUrl( - \Magento\Framework\UrlInterface::URL_TYPE_MEDIA - ) . $this->_newFile; - } - - return $url; + return $this->imageAsset->getUrl(); } /** @@ -741,9 +689,7 @@ public function getDestinationSubdir() */ public function isCached() { - if (is_string($this->_newFile)) { - return $this->_fileExists($this->_newFile); - } + return file_exists($this->imageAsset->getPath()); } /** @@ -940,18 +886,72 @@ protected function _fileExists($filename) */ public function getResizedImageInfo() { - $fileInfo = null; - if ($this->_newFile === true) { - $asset = $this->_assetRepo->createAsset( - "Magento_Catalog::images/product/placeholder/{$this->getDestinationSubdir()}.jpg" - ); - $img = $asset->getSourceFile(); - $fileInfo = getimagesize($img); + if ($this->isBaseFilePlaceholder() == true) { + $image = $this->imageAsset->getSourceFile(); } else { - if ($this->_mediaDirectory->isFile($this->_mediaDirectory->getAbsolutePath($this->_newFile))) { - $fileInfo = getimagesize($this->_mediaDirectory->getAbsolutePath($this->_newFile)); - } + $image = $this->imageAsset->getPath(); } - return $fileInfo; + return getimagesize($image); + } + + /** + * @return \Magento\Catalog\Model\View\Asset\ImageFactory + */ + private function getViewAssetImageFactory() + { + if ($this->viewAssetImageFactory == null) { + $this->viewAssetImageFactory = ObjectManager::getInstance()->get( + \Magento\Catalog\Model\View\Asset\ImageFactory::class + ); + } + + return $this->viewAssetImageFactory; + } + + /** + * @return \Magento\Catalog\Model\View\Asset\PlaceholderFactory + */ + private function getViewAssetPlaceholderFactory() + { + if ($this->viewAssetPlaceholderFactory == null) { + $this->viewAssetPlaceholderFactory = ObjectManager::getInstance()->get( + \Magento\Catalog\Model\View\Asset\PlaceholderFactory::class + ); + } + + return $this->viewAssetPlaceholderFactory; + } + + /** + * Retrieve misc params based on all image attributes + * + * @return array + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + private function getMiscParams() + { + $miscParams = [ + 'image_type' => $this->getDestinationSubdir(), + 'image_height' => $this->getHeight(), + 'image_width' => $this->getWidth(), + 'keep_aspect_ratio' => ($this->_keepAspectRatio ? '' : 'non') . 'proportional', + 'keep_frame' => ($this->_keepFrame ? '' : 'no') . 'frame', + 'keep_transparency' => ($this->_keepTransparency ? '' : 'no') . 'transparency', + 'constrain_only' => ($this->_constrainOnly ? 'do' : 'not') . 'constrainonly', + 'background' => $this->_rgbToString($this->_backgroundColor), + 'angle' => $this->_angle, + 'quality' => $this->_quality, + ]; + + // if has watermark add watermark params to hash + if ($this->getWatermarkFile()) { + $miscParams['watermark_file'] = $this->getWatermarkFile(); + $miscParams['watermark_image_opacity'] = $this->getWatermarkImageOpacity(); + $miscParams['watermark_position'] = $this->getWatermarkPosition(); + $miscParams['watermark_width'] = $this->getWatermarkWidth(); + $miscParams['watermark_height'] = $this->getWatermarkHeight(); + } + + return $miscParams; } } diff --git a/app/code/Magento/Catalog/Model/Product/Image/Cache.php b/app/code/Magento/Catalog/Model/Product/Image/Cache.php index 830e8be2f3852..2a5316583ff6e 100644 --- a/app/code/Magento/Catalog/Model/Product/Image/Cache.php +++ b/app/code/Magento/Catalog/Model/Product/Image/Cache.php @@ -1,6 +1,6 @@ metadataPool = $metadataPool; $this->linkResource = $linkResource; $this->productLinkRepository = $productLinkRepository; @@ -54,12 +53,18 @@ public function __construct( */ public function execute($entityType, $entity) { - /** @var \Magento\Catalog\Api\Data\ProductInterface $entity*/ - foreach ($this->productLinkRepository->getList($entity) as $link) { - $this->productLinkRepository->delete($link); + $link = $entity->getData($this->metadataPool->getMetadata($entityType)->getLinkField()); + if ($this->linkResource->hasProductLinks($link)) { + /** @var \Magento\Catalog\Api\Data\ProductInterface $entity*/ + foreach ($this->productLinkRepository->getList($entity) as $link) { + $this->productLinkRepository->delete($link); + } } - foreach ($entity->getProductLinks() as $link) { - $this->productLinkRepository->save($link); + $productLinks = $entity->getProductLinks(); + if (count($productLinks) > 0) { + foreach ($entity->getProductLinks() as $link) { + $this->productLinkRepository->save($link); + } } return $entity; } diff --git a/app/code/Magento/Catalog/Model/Product/LinkTypeProvider.php b/app/code/Magento/Catalog/Model/Product/LinkTypeProvider.php index 8a878e68957dc..e9f3e490bec57 100644 --- a/app/code/Magento/Catalog/Model/Product/LinkTypeProvider.php +++ b/app/code/Magento/Catalog/Model/Product/LinkTypeProvider.php @@ -2,7 +2,7 @@ /** * Collection of the available product link types * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model\Product; diff --git a/app/code/Magento/Catalog/Model/Product/Media/AttributeManagement.php b/app/code/Magento/Catalog/Model/Product/Media/AttributeManagement.php index 1bcc025fa2e7c..22f7690c8fc10 100644 --- a/app/code/Magento/Catalog/Model/Product/Media/AttributeManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Media/AttributeManagement.php @@ -1,6 +1,6 @@ productRepository->get($productSku); $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class); $option->setData('product_id', $product->getData($metadata->getLinkField())); - $option->setOptionId(null); + $option->setData('store_id', $product->getStoreId()); + + if ($option->getOptionId()) { + $options = $product->getOptions(); + if (!$options) { + $options = $this->getProductOptions($product); + } + + $persistedOption = array_filter($options, function ($iOption) use ($option) { + return $option->getOptionId() == $iOption->getOptionId(); + }); + $persistedOption = reset($persistedOption); + + if (!$persistedOption) { + throw new NoSuchEntityException(); + } + $originalValues = $persistedOption->getValues(); + $newValues = $option->getData('values'); + if ($newValues) { + $newValues = $this->markRemovedValues($newValues, $originalValues); + $option->setData('values', $newValues); + } + } $option->save(); return $option; } diff --git a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php index 7c32232b6591a..72241cbe6e701 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php @@ -1,6 +1,6 @@ getOptions(); + $optionIds = []; + + if ($options) { + $optionIds = array_map(function ($option) { + /** @var \Magento\Catalog\Model\Product\Option $option */ + return $option->getOptionId(); + }, $options); + } + /** @var \Magento\Catalog\Api\Data\ProductInterface $entity */ foreach ($this->optionRepository->getProductOptions($entity) as $option) { - $this->optionRepository->delete($option); + if (!in_array($option->getOptionId(), $optionIds)) { + $this->optionRepository->delete($option); + } } - if ($entity->getOptions()) { - foreach ($entity->getOptions() as $option) { + if ($options) { + foreach ($options as $option) { $this->optionRepository->save($option); } } + return $entity; } } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type.php b/app/code/Magento/Catalog/Model/Product/Option/Type.php index 4b59ca40dfd79..893b91021b818 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type.php @@ -1,6 +1,6 @@ _localeDate = $localeDate; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct($checkoutSession, $scopeConfig, $data); } @@ -269,7 +280,7 @@ public function prepareOptionValueForRequest($optionValue) $confItem = $this->getConfigurationItem(); $infoBuyRequest = $confItem->getOptionByCode('info_buyRequest'); try { - $value = unserialize($infoBuyRequest->getValue()); + $value = $this->serializer->unserialize($infoBuyRequest->getValue()); if (is_array($value) && isset($value['options']) && isset($value['options'][$this->getOption()->getId()]) ) { return $value['options'][$this->getOption()->getId()]; diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php index 9c54207a3645d..b54266e17b402 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php @@ -1,6 +1,6 @@ _itemOptionFactory = $itemOptionFactory; $this->_urlBuilder = $urlBuilder; $this->_escaper = $escaper; $this->_coreFileStorageDatabase = $coreFileStorageDatabase; + $this->filesystem = $filesystem ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Filesystem::class); + $this->_rootDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); $this->validatorInfo = $validatorInfo; $this->validatorFile = $validatorFile; + $this->serializer = $serializer ? $serializer : ObjectManager::getInstance()->get(Json::class); parent::__construct($checkoutSession, $scopeConfig, $data); } @@ -265,7 +285,7 @@ public function prepareForCart() // Save option in request, because we have no $_FILES['options'] $requestOptions[$this->getOption()->getId()] = $value; - $result = serialize($value); + $result = $this->serializer->serialize($value); } else { /* * Clear option info from request, so it won't be stored in our db upon @@ -296,7 +316,7 @@ public function getFormattedOptionValue($optionValue) { if ($this->_formattedOptionValue === null) { try { - $value = unserialize($optionValue); + $value = $this->serializer->unserialize($optionValue); $customOptionUrlParams = $this->getCustomOptionUrlParams() ? $this->getCustomOptionUrlParams() : [ 'id' => $this->getConfigurationItemOption()->getId(), @@ -306,7 +326,7 @@ public function getFormattedOptionValue($optionValue) $value['url'] = ['route' => $this->_customOptionDownloadUrl, 'params' => $customOptionUrlParams]; $this->_formattedOptionValue = $this->_getOptionHtml($value); - $this->getConfigurationItemOption()->setValue(serialize($value)); + $this->getConfigurationItemOption()->setValue($this->serializer->serialize($value)); return $this->_formattedOptionValue; } catch (\Exception $e) { return $optionValue; @@ -354,7 +374,7 @@ protected function _unserializeValue($value) if (is_array($value)) { return $value; } elseif (is_string($value) && !empty($value)) { - return unserialize($value); + return $this->serializer->unserialize($value); } else { return []; } @@ -376,11 +396,13 @@ public function getPrintableOptionValue($optionValue) * * @param string $optionValue Prepared for cart option value * @return string + * + * @deprecated */ public function getEditableOptionValue($optionValue) { try { - $value = unserialize($optionValue); + $value = $this->serializer->unserialize($optionValue); return sprintf( '%s [%d]', $this->_escaper->escapeHtml($value['title']), @@ -399,6 +421,8 @@ public function getEditableOptionValue($optionValue) * @return string|null * * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @deprecated */ public function parseOptionValue($optionValue, $productOptionValues) { @@ -407,7 +431,7 @@ public function parseOptionValue($optionValue, $productOptionValues) $confItemOptionId = $matches[1]; $option = $this->_itemOptionFactory->create()->load($confItemOptionId); try { - unserialize($option->getValue()); + $this->serializer->unserialize($option->getValue()); return $option->getValue(); } catch (\Exception $e) { return null; @@ -426,7 +450,7 @@ public function parseOptionValue($optionValue, $productOptionValues) public function prepareOptionValueForRequest($optionValue) { try { - $result = unserialize($optionValue); + $result = $this->serializer->unserialize($optionValue); return $result; } catch (\Exception $e) { return null; @@ -442,7 +466,7 @@ public function copyQuoteToOrder() { $quoteOption = $this->getConfigurationItemOption(); try { - $value = unserialize($quoteOption->getValue()); + $value = $this->serializer->unserialize($quoteOption->getValue()); if (!isset($value['quote_path'])) { throw new \Exception(); } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidateFactory.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidateFactory.php index 71011fe55072d..8d12349af0354 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidateFactory.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidateFactory.php @@ -1,6 +1,6 @@ getProduct()->getStoreId(); } foreach ($values as $value) { + if (isset($value['is_delete']) && (bool)$value['is_delete']) { + continue; + } $type = isset($value['price_type']) ? $value['price_type'] : null; $price = isset($value['price']) ? $value['price'] : null; $title = isset($value['title']) ? $value['title'] : null; diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/Text.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/Text.php index 84d2439097185..ebc7a6ba71e59 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/Text.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/Text.php @@ -1,6 +1,6 @@ getOption()->getStoreId() ); - $this->unsetData('option_type_id'); + if ($this->getData('is_delete') == '1') { if ($this->getId()) { $this->deleteValues($this->getId()); diff --git a/app/code/Magento/Catalog/Model/Product/Price/BasePrice.php b/app/code/Magento/Catalog/Model/Product/Price/BasePrice.php new file mode 100644 index 0000000000000..0b3976b3857fc --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/BasePrice.php @@ -0,0 +1,79 @@ +setData(self::PRICE, $price); + } + + /** + * {@inheritdoc} + */ + public function getPrice() + { + return $this->getData(self::PRICE); + } + + /** + * {@inheritdoc} + */ + public function setStoreId($storeId) + { + return $this->setData(self::STORE_ID, $storeId); + } + + /** + * {@inheritdoc} + */ + public function getStoreId() + { + return $this->getData(self::STORE_ID); + } + + /** + * {@inheritdoc} + */ + public function setSku($sku) + { + return $this->setData(self::SKU, $sku); + } + + /** + * {@inheritdoc} + */ + public function getSku() + { + return $this->getData(self::SKU); + } + + /** + * {@inheritdoc} + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * {@inheritdoc} + */ + public function setExtensionAttributes(\Magento\Catalog\Api\Data\BasePriceExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php new file mode 100644 index 0000000000000..097e1a13dc6f9 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php @@ -0,0 +1,228 @@ +pricePersistenceFactory = $pricePersistenceFactory; + $this->basePriceInterfaceFactory = $basePriceInterfaceFactory; + $this->productIdLocator = $productIdLocator; + $this->storeRepository = $storeRepository; + $this->productRepository = $productRepository; + $this->validationResult = $validationResult; + $this->allowedProductTypes = $allowedProductTypes; + $this->invalidSkuProcessor = $invalidSkuProcessor; + } + + /** + * {@inheritdoc} + */ + public function get(array $skus) + { + $skus = $this->invalidSkuProcessor->filterSkuList( + $skus, + $this->allowedProductTypes, + $this->priceTypeAllowed + ); + $rawPrices = $this->getPricePersistence()->get($skus); + $prices = []; + foreach ($rawPrices as $rawPrice) { + $price = $this->basePriceInterfaceFactory->create(); + $sku = $this->getPricePersistence() + ->retrieveSkuById($rawPrice[$this->getPricePersistence()->getEntityLinkField()], $skus); + $price->setSku($sku); + $price->setPrice($rawPrice['value']); + $price->setStoreId($rawPrice['store_id']); + $prices[] = $price; + } + + return $prices; + } + + /** + * {@inheritdoc} + */ + public function update(array $prices) + { + $prices = $this->retrieveValidPrices($prices); + $formattedPrices = []; + + foreach ($prices as $price) { + $ids = array_keys($this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()])[$price->getSku()]); + foreach ($ids as $id) { + $formattedPrices[] = [ + 'store_id' => $price->getStoreId(), + $this->getPricePersistence()->getEntityLinkField() => $id, + 'value' => $price->getPrice(), + ]; + } + } + + $this->getPricePersistence()->update($formattedPrices); + + return $this->validationResult->getFailedItems(); + } + + /** + * Get price persistence. + * + * @return PricePersistence + */ + private function getPricePersistence() + { + if (!$this->pricePersistence) { + $this->pricePersistence = $this->pricePersistenceFactory->create(['attributeCode' => $this->attributeCode]); + } + + return $this->pricePersistence; + } + + /** + * Retrieve valid prices that do not contain any errors. + * + * @param \Magento\Catalog\Api\Data\BasePriceInterface[] $prices + * @return array + */ + private function retrieveValidPrices(array $prices) + { + $skus = array_unique( + array_map(function ($price) { + return $price->getSku(); + }, $prices) + ); + $invalidSkus = $this->invalidSkuProcessor->retrieveInvalidSkuList( + $skus, + $this->allowedProductTypes, + $this->priceTypeAllowed + ); + + foreach ($prices as $id => $price) { + if (!$price->getSku() || in_array($price->getSku(), $invalidSkus)) { + $this->validationResult->addFailedItem( + $id, + __( + 'Invalid attribute %fieldName = %fieldValue.', + ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue'] + ), + ['fieldName' => 'SKU', 'fieldValue' => $price->getSku()] + ); + } + if (null === $price->getPrice() || $price->getPrice() < 0) { + $this->validationResult->addFailedItem( + $id, + __( + 'Invalid attribute %fieldName = %fieldValue.', + ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue'] + ), + ['fieldName' => 'Price', 'fieldValue' => $price->getPrice()] + ); + } + try { + $this->storeRepository->getById($price->getStoreId()); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + $this->validationResult->addFailedItem( + $id, + __( + 'Requested store is not found. Row ID: SKU = %SKU, Store ID: %storeId.', + ['SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] + ), + ['SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] + ); + } + } + + foreach ($this->validationResult->getFailedRowIds() as $id) { + unset($prices[$id]); + } + + return $prices; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/Cost.php b/app/code/Magento/Catalog/Model/Product/Price/Cost.php new file mode 100644 index 0000000000000..f3991bf188187 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/Cost.php @@ -0,0 +1,79 @@ +setData(self::COST, $cost); + } + + /** + * {@inheritdoc} + */ + public function getCost() + { + return $this->getData(self::COST); + } + + /** + * {@inheritdoc} + */ + public function setStoreId($storeId) + { + return $this->setData(self::STORE_ID, $storeId); + } + + /** + * {@inheritdoc} + */ + public function getStoreId() + { + return $this->getData(self::STORE_ID); + } + + /** + * {@inheritdoc} + */ + public function setSku($sku) + { + return $this->setData(self::SKU, $sku); + } + + /** + * {@inheritdoc} + */ + public function getSku() + { + return $this->getData(self::SKU); + } + + /** + * {@inheritdoc} + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * {@inheritdoc} + */ + public function setExtensionAttributes(\Magento\Catalog\Api\Data\CostExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php new file mode 100644 index 0000000000000..977d1948bbf0b --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php @@ -0,0 +1,219 @@ +pricePersistenceFactory = $pricePersistenceFactory; + $this->costInterfaceFactory = $costInterfaceFactory; + $this->productIdLocator = $productIdLocator; + $this->storeRepository = $storeRepository; + $this->validationResult = $validationResult; + $this->invalidSkuProcessor = $invalidSkuProcessor; + $this->allowedProductTypes = $allowedProductTypes; + } + + /** + * {@inheritdoc} + */ + public function get(array $skus) + { + $skus = $this->invalidSkuProcessor->filterSkuList($skus, $this->allowedProductTypes); + $rawPrices = $this->getPricePersistence()->get($skus); + $prices = []; + foreach ($rawPrices as $rawPrice) { + $price = $this->costInterfaceFactory->create(); + $sku = $this->getPricePersistence() + ->retrieveSkuById($rawPrice[$this->getPricePersistence()->getEntityLinkField()], $skus); + $price->setSku($sku); + $price->setCost($rawPrice['value']); + $price->setStoreId($rawPrice['store_id']); + $prices[] = $price; + } + + return $prices; + } + + /** + * {@inheritdoc} + */ + public function update(array $prices) + { + $prices = $this->retrieveValidPrices($prices); + $formattedPrices = []; + + foreach ($prices as $price) { + $productIdsBySkus = $this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()]); + $productIds = array_keys($productIdsBySkus[$price->getSku()]); + foreach ($productIds as $id) { + $formattedPrices[] = [ + 'store_id' => $price->getStoreId(), + $this->getPricePersistence()->getEntityLinkField() => $id, + 'value' => $price->getCost(), + ]; + } + } + + $this->getPricePersistence()->update($formattedPrices); + + return $this->validationResult->getFailedItems(); + } + + /** + * {@inheritdoc} + */ + public function delete(array $skus) + { + $skus = $this->invalidSkuProcessor->filterSkuList($skus, $this->allowedProductTypes); + $this->getPricePersistence()->delete($skus); + + return true; + } + + /** + * Get price persistence. + * + * @return PricePersistence + */ + private function getPricePersistence() + { + if (!$this->pricePersistence) { + $this->pricePersistence = $this->pricePersistenceFactory->create(['attributeCode' => $this->attributeCode]); + } + + return $this->pricePersistence; + } + + /** + * Retrieve valid prices that do not contain any errors. + * + * @param array $prices + * @return array + */ + private function retrieveValidPrices(array $prices) + { + $skus = array_unique( + array_map(function ($price) { + return $price->getSku(); + }, $prices) + ); + $invalidSkus = $this->invalidSkuProcessor->retrieveInvalidSkuList($skus, $this->allowedProductTypes); + + foreach ($prices as $id => $price) { + if (!$price->getSku() || in_array($price->getSku(), $invalidSkus)) { + $this->validationResult->addFailedItem( + $id, + __( + 'Invalid attribute %fieldName = %fieldValue.', + ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue'] + ), + ['fieldName' => 'SKU', 'fieldValue' => $price->getSku()] + ); + } + if (null === $price->getCost() || $price->getCost() < 0) { + $this->validationResult->addFailedItem( + $id, + __( + 'Invalid attribute Cost = %cost. Row ID: SKU = %SKU, Store ID: %storeId.', + ['cost' => $price->getCost(), 'SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] + ), + ['cost' => $price->getCost(), 'SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] + ); + } + try { + $this->storeRepository->getById($price->getStoreId()); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + $this->validationResult->addFailedItem( + $id, + __( + 'Requested store is not found. Row ID: SKU = %SKU, Store ID: %storeId.', + ['SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] + ), + ['SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] + ); + } + } + + foreach ($this->validationResult->getFailedRowIds() as $id) { + unset($prices[$id]); + } + + return $prices; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php b/app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php new file mode 100644 index 0000000000000..87effd3a58aba --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php @@ -0,0 +1,228 @@ +attributeResource = $attributeResource; + $this->attributeRepository = $attributeRepository; + $this->attributeCode = $attributeCode; + $this->productIdLocator = $productIdLocator; + $this->metadataPool = $metadataPool; + } + + /** + * Get prices by SKUs. + * + * @param array $skus + * @return array + */ + public function get(array $skus) + { + $ids = $this->retrieveAffectedIds($skus); + $select = $this->attributeResource->getConnection() + ->select() + ->from($this->attributeResource->getTable($this->table)); + return $this->attributeResource->getConnection()->fetchAll( + $select->where($this->getEntityLinkField() . ' IN (?)', $ids) + ->where('attribute_id = ?', $this->getAttributeId()) + ); + } + + /** + * Update prices. + * + * @param array $prices + * @return void + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function update(array $prices) + { + array_walk($prices, function (&$price) { + return $price['attribute_id'] = $this->getAttributeId(); + }); + $connection = $this->attributeResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) { + $this->attributeResource->getConnection()->insertOnDuplicate( + $this->attributeResource->getTable($this->table), + $pricesBunch, + ['value'] + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotSaveException( + __('Could not save Prices.'), + $e + ); + } + } + + /** + * Delete product attribute by SKU. + * + * @param array $skus + * @return void + * @throws \Magento\Framework\Exception\CouldNotDeleteException + */ + public function delete(array $skus) + { + $ids = $this->retrieveAffectedIds($skus); + $connection = $this->attributeResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) { + $this->attributeResource->getConnection()->delete( + $this->attributeResource->getTable($this->table), + [ + 'attribute_id = ?' => $this->getAttributeId(), + $this->getEntityLinkField() . ' IN (?)' => $idsBunch + ] + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotDeleteException( + __('Could not delete Prices'), + $e + ); + } + } + + /** + * Retrieve SKU by product ID. + * + * @param int $id + * @param array $skus + * @return int|null + */ + public function retrieveSkuById($id, $skus) + { + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) { + if (false !== array_key_exists($id, $ids)) { + return $sku; + } + } + + return null; + } + + /** + * Get attribute ID. + * + * @return int + */ + private function getAttributeId() + { + if (!$this->attributeId) { + $this->attributeId = $this->attributeRepository->get($this->attributeCode)->getAttributeId(); + } + + return $this->attributeId; + } + + /** + * Retrieve affected product IDs. + * + * @param array $skus + * @return array + */ + private function retrieveAffectedIds(array $skus) + { + $affectedIds = []; + + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $productIds) { + $affectedIds = array_merge($affectedIds, array_keys($productIds)); + } + + return array_unique($affectedIds); + } + + /** + * Get link field. + * + * @return string + */ + public function getEntityLinkField() + { + return $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/PriceUpdateResult.php b/app/code/Magento/Catalog/Model/Product/Price/PriceUpdateResult.php new file mode 100644 index 0000000000000..6922879d0fa93 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/PriceUpdateResult.php @@ -0,0 +1,64 @@ +getData(self::MESSAGE); + } + + /** + * {@inheritdoc} + */ + public function setMessage($message) + { + return $this->setData(self::MESSAGE, $message); + } + + /** + * {@inheritdoc} + */ + public function getParameters() + { + return $this->getData(self::PARAMETERS); + } + + /** + * {@inheritdoc} + */ + public function setParameters(array $parameters) + { + return $this->setData(self::PARAMETERS, $parameters); + } + + /** + * {@inheritdoc} + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * {@inheritdoc} + */ + public function setExtensionAttributes( + \Magento\Catalog\Api\Data\PriceUpdateResultExtensionInterface $extensionAttributes + ) { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/SpecialPrice.php b/app/code/Magento/Catalog/Model/Product/Price/SpecialPrice.php new file mode 100644 index 0000000000000..e80f5ad29657f --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/SpecialPrice.php @@ -0,0 +1,112 @@ +setData(self::PRICE, $price); + } + + /** + * {@inheritdoc} + */ + public function getPrice() + { + return $this->getData(self::PRICE); + } + + /** + * {@inheritdoc} + */ + public function setStoreId($storeId) + { + return $this->setData(self::STORE_ID, $storeId); + } + + /** + * {@inheritdoc} + */ + public function getStoreId() + { + return $this->getData(self::STORE_ID); + } + + /** + * {@inheritdoc} + */ + public function setSku($sku) + { + return $this->setData(self::SKU, $sku); + } + + /** + * {@inheritdoc} + */ + public function getSku() + { + return $this->getData(self::SKU); + } + + /** + * {@inheritdoc} + */ + public function setPriceFrom($datetime) + { + return $this->setData(self::PRICE_FROM, $datetime); + } + + /** + * {@inheritdoc} + */ + public function getPriceFrom() + { + return $this->getData(self::PRICE_FROM); + } + + /** + * {@inheritdoc} + */ + public function setPriceTo($datetime) + { + return $this->setData(self::PRICE_TO, $datetime); + } + + /** + * {@inheritdoc} + */ + public function getPriceTo() + { + return $this->getData(self::PRICE_TO); + } + + /** + * {@inheritdoc} + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * {@inheritdoc} + */ + public function setExtensionAttributes( + \Magento\Catalog\Api\Data\SpecialPriceExtensionInterface $extensionAttributes + ) { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php new file mode 100644 index 0000000000000..a039aa93a61de --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php @@ -0,0 +1,298 @@ +specialPriceResource = $specialPriceResource; + $this->specialPriceFactory = $specialPriceFactory; + $this->productIdLocator = $productIdLocator; + $this->storeRepository = $storeRepository; + $this->validationResult = $validationResult; + $this->invalidSkuProcessor = $invalidSkuProcessor; + $this->allowedProductTypes = $allowedProductTypes; + } + + /** + * {@inheritdoc} + */ + public function get(array $skus) + { + $skus = $this->invalidSkuProcessor->filterSkuList($skus, $this->allowedProductTypes); + $rawPrices = $this->specialPriceResource->get($skus); + + $prices = []; + foreach ($rawPrices as $rawPrice) { + /** @var \Magento\Catalog\Api\Data\SpecialPriceInterface $price */ + $price = $this->specialPriceFactory->create(); + $sku = isset($rawPrice['sku']) + ? $rawPrice['sku'] + : $this->retrieveSkuById($rawPrice[$this->specialPriceResource->getEntityLinkField()], $skus); + $price->setSku($sku); + $price->setPrice($rawPrice['value']); + $price->setStoreId($rawPrice['store_id']); + $price->setPriceFrom($rawPrice['price_from']); + $price->setPriceTo($rawPrice['price_to']); + $prices[] = $price; + } + + return $prices; + } + + /** + * {@inheritdoc} + */ + public function update(array $prices) + { + $prices = $this->retrieveValidPrices($prices); + $this->specialPriceResource->update($prices); + + return $this->validationResult->getFailedItems(); + } + + /** + * {@inheritdoc} + */ + public function delete(array $prices) + { + $prices = $this->retrieveValidPrices($prices); + $this->specialPriceResource->delete($prices); + + return $this->validationResult->getFailedItems(); + } + + /** + * Retrieve prices with correct values. + * + * @param array $prices + * @return array + */ + private function retrieveValidPrices(array $prices) + { + $skus = array_unique( + array_map(function ($price) { + return $price->getSku(); + }, $prices) + ); + $failedSkus = $this->invalidSkuProcessor->retrieveInvalidSkuList($skus, $this->allowedProductTypes); + + foreach ($prices as $key => $price) { + if (!$price->getSku() || in_array($price->getSku(), $failedSkus)) { + $this->validationResult->addFailedItem( + $key, + __( + 'Requested product doesn\'t exist. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] + ), + [ + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] + ); + } + $this->checkPrice($price, $key); + $this->checkDate($price, $price->getPriceFrom(), 'Price From', $key); + $this->checkDate($price, $price->getPriceTo(), 'Price To', $key); + try { + $this->storeRepository->getById($price->getStoreId()); + } catch (NoSuchEntityException $e) { + $this->validationResult->addFailedItem( + $key, + __( + 'Requested store is not found. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] + ), + [ + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] + ); + } + } + + foreach ($this->validationResult->getFailedRowIds() as $id) { + unset($prices[$id]); + } + + return $prices; + } + + /** + * Check that date value is correct and add error to aggregator if it contains incorrect data. + * + * @param \Magento\Catalog\Api\Data\SpecialPriceInterface $price + * @param string $value + * @param string $label + * @param int $key + * @return void + */ + private function checkDate(\Magento\Catalog\Api\Data\SpecialPriceInterface $price, $value, $label, $key) + { + if ($value && !$this->isCorrectDateValue($value)) { + $this->validationResult->addFailedItem( + $key, + __( + 'Invalid attribute %label = %priceTo. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'label' => $label, + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] + ), + [ + 'label' => $label, + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] + ); + } + } + + /** + * Check that provided price value is not empty and not lower then zero and add error to aggregator if price + * contains not valid data. + * + * @param \Magento\Catalog\Api\Data\SpecialPriceInterface $price + * @param int $key + * @return void + */ + private function checkPrice(\Magento\Catalog\Api\Data\SpecialPriceInterface $price, $key) + { + if (null === $price->getPrice() || $price->getPrice() < 0) { + $this->validationResult->addFailedItem( + $key, + __( + 'Invalid attribute Price = %price. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'price' => $price->getPrice(), + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] + ), + [ + 'price' => $price->getPrice(), + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] + ); + } + } + + /** + * Retrieve SKU by product ID. + * + * @param int $productId + * @param array $skus + * @return string|null + */ + private function retrieveSkuById($productId, array $skus) + { + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) { + if (isset($ids[$productId])) { + return $sku; + } + } + + return null; + } + + /** + * Check that date value is correct. + * + * @param string $date + * @return bool + */ + private function isCorrectDateValue($date) + { + $actualDate = date('Y-m-d H:i:s', strtotime($date)); + return $actualDate && $actualDate === $date; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPrice.php b/app/code/Magento/Catalog/Model/Product/Price/TierPrice.php new file mode 100644 index 0000000000000..0b61ae4bcbf0c --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPrice.php @@ -0,0 +1,127 @@ +setData(self::PRICE, $price); + } + + /** + * {@inheritdoc} + */ + public function getPrice() + { + return $this->getData(self::PRICE); + } + + /** + * {@inheritdoc} + */ + public function setPriceType($type) + { + return $this->setData(self::PRICE_TYPE, $type); + } + + /** + * {@inheritdoc} + */ + public function getPriceType() + { + return $this->getData(self::PRICE_TYPE); + } + + /** + * {@inheritdoc} + */ + public function setWebsiteId($websiteId) + { + return $this->setData(self::WEBSITE_ID, $websiteId); + } + + /** + * {@inheritdoc} + */ + public function getWebsiteId() + { + return $this->getData(self::WEBSITE_ID); + } + + /** + * {@inheritdoc} + */ + public function setSku($sku) + { + return $this->setData(self::SKU, $sku); + } + + /** + * {@inheritdoc} + */ + public function getSku() + { + return $this->getData(self::SKU); + } + + /** + * {@inheritdoc} + */ + public function setCustomerGroup($group) + { + return $this->setData(self::CUSTOMER_GROUP, $group); + } + + /** + * {@inheritdoc} + */ + public function getCustomerGroup() + { + return $this->getData(self::CUSTOMER_GROUP); + } + + /** + * {@inheritdoc} + */ + public function setQuantity($quantity) + { + return $this->setData(self::QUANTITY, $quantity); + } + + /** + * {@inheritdoc} + */ + public function getQuantity() + { + return $this->getData(self::QUANTITY); + } + + /** + * {@inheritdoc} + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * {@inheritdoc} + */ + public function setExtensionAttributes(\Magento\Catalog\Api\Data\TierPriceExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php new file mode 100644 index 0000000000000..1cc608f662570 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php @@ -0,0 +1,169 @@ +tierPriceFactory = $tierPriceFactory; + $this->tierPricePersistence = $tierPricePersistence; + $this->customerGroupRepository = $customerGroupRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->filterBuilder = $filterBuilder; + } + + /** + * Create populated tier price DTO. + * + * @param array $rawPrice + * @param string $sku + * @return \Magento\Catalog\Api\Data\TierPriceInterface + */ + public function create(array $rawPrice, $sku) + { + $price = $this->tierPriceFactory->create(); + $price->setPrice(isset($rawPrice['percentage_value']) ? $rawPrice['percentage_value'] : $rawPrice['value']); + $price->setPriceType( + isset($rawPrice['percentage_value']) + ? TierPriceInterface::PRICE_TYPE_DISCOUNT + : TierPriceInterface::PRICE_TYPE_FIXED + ); + $price->setWebsiteId($rawPrice['website_id']); + $price->setSku($sku); + $price->setCustomerGroup( + $rawPrice['all_groups'] == $this->allGroupsId + ? $this->allGroupsValue + : $this->customerGroupRepository->getById($rawPrice['customer_group_id'])->getCode() + ); + $price->setQuantity($rawPrice['qty']); + + return $price; + } + + /** + * Build tier price skeleton that has DB consistent format. + * + * @param TierPriceInterface $price + * @param int $id + * @return array + */ + public function createSkeleton(TierPriceInterface $price, $id) + { + return [ + $this->tierPricePersistence->getEntityLinkField() => $id, + 'all_groups' => $this->retrievePriceForAllGroupsValue($price), + 'customer_group_id' => $this->retrievePriceForAllGroupsValue($price) === $this->allGroupsId + ? 0 + : $this->retrieveGroupValue(strtolower($price->getCustomerGroup())), + 'qty' => $price->getQuantity(), + 'value' => $price->getPriceType() === TierPriceInterface::PRICE_TYPE_FIXED + ? $price->getPrice() + : 0.00, + 'percentage_value' => $price->getPriceType() === TierPriceInterface::PRICE_TYPE_DISCOUNT + ? $price->getPrice() + : null, + 'website_id' => $price->getWebsiteId() + ]; + } + + /** + * Retrieve price for all groups value. + * + * @param TierPriceInterface $price + * @return int + */ + private function retrievePriceForAllGroupsValue(TierPriceInterface $price) + { + return strcasecmp($price->getCustomerGroup(), $this->allGroupsValue) === 0 ? $this->allGroupsId : 0; + } + + /** + * Retrieve customer group id by code. + * + * @param string $code + * @return int + * @throws NoSuchEntityException + */ + private function retrieveGroupValue($code) + { + if (!isset($this->customerGroupsByCode[$code])) { + $searchCriteria = $this->searchCriteriaBuilder->addFilters( + [ + $this->filterBuilder->setField('customer_group_code')->setValue($code)->create() + ] + ); + $items = $this->customerGroupRepository->getList($searchCriteria->create())->getItems(); + $item = array_shift($items); + $this->customerGroupsByCode[strtolower($item->getCode())] = $item->getId(); + } + + return $this->customerGroupsByCode[$code]; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php b/app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php new file mode 100644 index 0000000000000..a7149a60c41a7 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php @@ -0,0 +1,166 @@ +tierpriceResource = $tierpriceResource; + $this->metadataPool = $metadataPool; + } + + /** + * Get tier prices by product IDs. + * + * @param array $ids + * @return array + */ + public function get(array $ids) + { + $select = $this->tierpriceResource->getConnection()->select()->from($this->tierpriceResource->getMainTable()); + return $this->tierpriceResource->getConnection()->fetchAll( + $select->where($this->getEntityLinkField() . ' IN (?)', $ids) + ); + } + + /** + * Update tier prices. + * + * @param array $prices + * @return void + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function update(array $prices) + { + $connection = $this->tierpriceResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) { + $this->tierpriceResource->getConnection()->insertOnDuplicate( + $this->tierpriceResource->getMainTable(), + $pricesBunch, + ['value', 'percentage_value'] + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotSaveException( + __('Could not save Tier Prices'), + $e + ); + } + } + + /** + * Replace prices. + * + * @param array $prices + * @param array $ids + * @return void + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function replace(array $prices, array $ids) + { + $connection = $this->tierpriceResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) { + $this->tierpriceResource->getConnection()->delete( + $this->tierpriceResource->getMainTable(), + [$this->getEntityLinkField() . ' IN (?)' => $idsBunch] + ); + } + + foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) { + $this->tierpriceResource->getConnection()->insertMultiple( + $this->tierpriceResource->getMainTable(), + $pricesBunch + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotSaveException( + __('Could not replace Tier Prices'), + $e + ); + } + } + + /** + * Delete tier prices by IDs. + * + * @param array $ids + * @return void + * @throws \Magento\Framework\Exception\CouldNotDeleteException + */ + public function delete(array $ids) + { + $connection = $this->tierpriceResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) { + $this->tierpriceResource->getConnection()->delete( + $this->tierpriceResource->getMainTable(), + ['value_id IN (?)' => $idsBunch] + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotDeleteException( + __('Could not delete Tier Prices'), + $e + ); + } + } + + /** + * Get link field. + * + * @return string + */ + public function getEntityLinkField() + { + return $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php new file mode 100644 index 0000000000000..9df60a63007b1 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -0,0 +1,360 @@ +tierPricePersistence = $tierPricePersistence; + $this->tierPriceValidator = $tierPriceValidator; + $this->tierPriceFactory = $tierPriceFactory; + $this->priceIndexer = $priceIndexer; + $this->productIdLocator = $productIdLocator; + $this->config = $config; + $this->typeList = $typeList; + } + + /** + * {@inheritdoc} + */ + public function get(array $skus) + { + $skus = $this->tierPriceValidator->validateSkus($skus); + + return $this->getExistingPrices($skus); + } + + /** + * {@inheritdoc} + */ + public function update(array $prices) + { + $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); + $skus = array_unique( + array_map(function ($price) { + return $price->getSku(); + }, $prices) + ); + $result = $this->tierPriceValidator->retrieveValidationResult($prices, $this->getExistingPrices($skus, true)); + $prices = $this->removeIncorrectPrices($prices, $result->getFailedRowIds()); + $formattedPrices = $this->retrieveFormattedPrices($prices); + $this->tierPricePersistence->update($formattedPrices); + $this->reindexPrices($affectedIds); + $this->invalidateFullPageCache(); + + return $result->getFailedItems(); + } + + /** + * {@inheritdoc} + */ + public function replace(array $prices) + { + $result = $this->tierPriceValidator->retrieveValidationResult($prices); + $prices = $this->removeIncorrectPrices($prices, $result->getFailedRowIds()); + $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); + $formattedPrices = $this->retrieveFormattedPrices($prices); + $this->tierPricePersistence->replace($formattedPrices, $affectedIds); + $this->reindexPrices($affectedIds); + $this->invalidateFullPageCache(); + + return $result->getFailedItems(); + } + + /** + * {@inheritdoc} + */ + public function delete(array $prices) + { + $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); + $result = $this->tierPriceValidator->retrieveValidationResult($prices); + $prices = $this->removeIncorrectPrices($prices, $result->getFailedRowIds()); + $priceIds = $this->retrieveAffectedPriceIds($prices); + $this->tierPricePersistence->delete($priceIds); + $this->reindexPrices($affectedIds); + $this->invalidateFullPageCache(); + + return $result->getFailedItems(); + } + + /** + * Get existing prices by SKUs. + * + * @param array $skus + * @param bool $groupBySku [optional] + * @return array + */ + private function getExistingPrices(array $skus, $groupBySku = false) + { + $ids = $this->retrieveAffectedIds($skus); + $rawPrices = $this->tierPricePersistence->get($ids); + $prices = []; + + foreach ($rawPrices as $rawPrice) { + $sku = $this->retrieveSkuById($rawPrice[$this->tierPricePersistence->getEntityLinkField()], $skus); + $price = $this->tierPriceFactory->create($rawPrice, $sku); + if ($groupBySku) { + $prices[$sku][] = $price; + } else { + $prices[] = $price; + } + } + + return $prices; + } + + /** + * Retrieve formatted prices. + * + * @param array $prices + * @return array + */ + private function retrieveFormattedPrices(array $prices) + { + $formattedPrices = []; + + foreach ($prices as $price) { + $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()]); + $ids = array_keys($idsBySku[$price->getSku()]); + foreach ($ids as $id) { + $formattedPrices[] = $this->tierPriceFactory->createSkeleton($price, $id); + } + } + + return $formattedPrices; + } + + /** + * Retrieve affected product IDs for prices. + * + * @param TierPriceInterface[] $prices + * @return array + */ + private function retrieveAffectedProductIdsForPrices(array $prices) + { + $skus = array_unique( + array_map(function ($price) { + return $price->getSku(); + }, $prices) + ); + + return $this->retrieveAffectedIds($skus); + } + + /** + * Retrieve affected product IDs. + * + * @param array $skus + * @return array + */ + private function retrieveAffectedIds(array $skus) + { + $affectedIds = []; + + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $productId) { + $affectedIds = array_merge($affectedIds, array_keys($productId)); + } + + return array_unique($affectedIds); + } + + /** + * Retrieve affected price IDs. + * + * @param array $prices + * @return array + */ + private function retrieveAffectedPriceIds(array $prices) + { + $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); + $formattedPrices = $this->retrieveFormattedPrices($prices); + $existingPrices = $this->tierPricePersistence->get($affectedIds); + $priceIds = []; + + foreach ($formattedPrices as $price) { + $priceIds[] = $this->retrievePriceId($price, $existingPrices); + } + + return $priceIds; + } + + /** + * Look through provided price in list of existing prices and retrieve it's Id. + * + * @param array $price + * @param array $existingPrices + * @return int|null + */ + private function retrievePriceId(array $price, array $existingPrices) + { + $linkField = $this->tierPricePersistence->getEntityLinkField(); + + foreach ($existingPrices as $existingPrice) { + if ($existingPrice['all_groups'] == $price['all_groups'] + && $existingPrice['customer_group_id'] == $price['customer_group_id'] + && $existingPrice['qty'] == $price['qty'] + && $this->isCorrectPriceValue($existingPrice, $price) + && $existingPrice[$linkField] == $price[$linkField] + ) { + return $existingPrice['value_id']; + } + } + + return null; + } + + /** + * Check that price value or price percentage value is not equal to 0 and is not similar with existing value. + * + * @param array $existingPrice + * @param array $price + * @return bool + */ + private function isCorrectPriceValue(array $existingPrice, array $price) + { + return ($existingPrice['value'] != 0 && $existingPrice['value'] == $price['value']) + || ($existingPrice['percentage_value'] !== null + && $existingPrice['percentage_value'] == $price['percentage_value']); + } + + /** + * Retrieve SKU by product ID. + * + * @param int $id + * @param array $skus + * @return string|null + */ + private function retrieveSkuById($id, $skus) + { + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) { + if (isset($ids[$id])) { + return $sku; + } + } + + return null; + } + + /** + * Invalidate full page cache. + * + * @return void + */ + private function invalidateFullPageCache() + { + if ($this->config->isEnabled()) { + $this->typeList->invalidate('full_page'); + } + } + + /** + * Reindex prices. + * + * @param array $ids + * @return void + */ + private function reindexPrices(array $ids) + { + foreach (array_chunk($ids, $this->indexerChunkValue) as $affectedIds) { + $this->priceIndexer->execute($affectedIds); + } + } + + /** + * Remove prices from price list by id list. + * + * @param array $prices + * @param array $ids + * @return array + */ + private function removeIncorrectPrices(array $prices, array $ids) + { + foreach ($ids as $id) { + unset($prices[$id]); + } + + return $prices; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/Validation/InvalidSkuProcessor.php b/app/code/Magento/Catalog/Model/Product/Price/Validation/InvalidSkuProcessor.php new file mode 100644 index 0000000000000..08d75616ab5ca --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/Validation/InvalidSkuProcessor.php @@ -0,0 +1,78 @@ +productIdLocator = $productIdLocator; + $this->productRepository = $productRepository; + } + + /** + * Retrieve not found or invalid SKUs which product types are included to allowed types list. + * + * @param array $skus + * @param array $allowedProductTypes + * @param int|null $allowedPriceTypeValue + * @return array + */ + public function retrieveInvalidSkuList(array $skus, array $allowedProductTypes, $allowedPriceTypeValue = null) + { + $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus); + $existingSkus = array_keys($idsBySku); + $skuDiff = array_udiff( + $skus, + $existingSkus, + 'strcasecmp' + ); + + foreach ($idsBySku as $sku => $ids) { + foreach ($ids as $type) { + $valueTypeIsAllowed = false; + + if ($allowedPriceTypeValue + && $type == \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE + && $this->productRepository->get($sku)->getPriceType() != $allowedPriceTypeValue + ) { + $valueTypeIsAllowed = true; + } + + if (!in_array($type, $allowedProductTypes) || $valueTypeIsAllowed) { + $skuDiff[] = $sku; + break; + } + } + } + + return $skuDiff; + } + + /** + * Filter invalid values in SKUs list. + * + * @param array $skus + * @param array $allowedProductTypes + * @param int|null $allowedPriceTypeValue + * @return array + */ + public function filterSkuList(array $skus, array $allowedProductTypes, $allowedPriceTypeValue = null) + { + $failedItems = $this->retrieveInvalidSkuList($skus, $allowedProductTypes, $allowedPriceTypeValue); + return array_diff($skus, $failedItems); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/Validation/Result.php b/app/code/Magento/Catalog/Model/Product/Price/Validation/Result.php new file mode 100644 index 0000000000000..fd02d72f51036 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/Validation/Result.php @@ -0,0 +1,82 @@ +priceUpdateResultFactory = $priceUpdateResultFactory; + } + + /** + * Add failed price identified, message and message parameters, that occurred during price update. + * + * @param int $id Failed price identified. + * @param string $message Failure reason message. + * @param array $parameters (optional). Placeholder values in ['placeholder key' => 'placeholder value'] format + * for failure reason message. + * @return void + */ + public function addFailedItem($id, $message, array $parameters = []) + { + $this->failedItems[$id][] = [ + 'message' => $message, + 'parameters' => $parameters + ]; + } + + /** + * Get ids of rows, that contained errors during price update. + * + * @return int[] + */ + public function getFailedRowIds() + { + return $this->failedItems ? array_keys($this->failedItems) : []; + } + + /** + * Get price update errors, that occurred during price update. + * + * @return \Magento\Catalog\Api\Data\PriceUpdateResultInterface[] + */ + public function getFailedItems() + { + $failedItems = []; + + foreach ($this->failedItems as $items) { + foreach ($items as $failedRecord) { + $resultItem = $this->priceUpdateResultFactory->create(); + $resultItem->setMessage($failedRecord['message']); + $resultItem->setParameters($failedRecord['parameters']); + $failedItems[] = $resultItem; + } + } + + return $failedItems; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/Validation/TierPriceValidator.php b/app/code/Magento/Catalog/Model/Product/Price/Validation/TierPriceValidator.php new file mode 100644 index 0000000000000..b39423c8894de --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/Validation/TierPriceValidator.php @@ -0,0 +1,478 @@ +productIdLocator = $productIdLocator; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->filterBuilder = $filterBuilder; + $this->customerGroupRepository = $customerGroupRepository; + $this->websiteRepository = $websiteRepository; + $this->tierPricePersistence = $tierPricePersistence; + $this->validationResult = $validationResult; + $this->invalidSkuProcessor = $invalidSkuProcessor; + $this->allowedProductTypes = $allowedProductTypes; + } + + /** + * Validate SKU. + * + * @param array $skus + * @return array + */ + public function validateSkus(array $skus) + { + return $this->invalidSkuProcessor->filterSkuList($skus, $this->allowedProductTypes); + } + + /** + * Validate that prices have appropriate values and are unique and return result. + * + * @param array $prices + * @param array $existingPrices + * @return \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult + */ + public function retrieveValidationResult(array $prices, array $existingPrices = []) + { + $validationResult = clone $this->validationResult; + $skus = array_unique( + array_map(function ($price) { + return $price->getSku(); + }, $prices) + ); + $skuDiff = $this->invalidSkuProcessor->retrieveInvalidSkuList($skus, $this->allowedProductTypes); + $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus); + + $pricesBySku = []; + + foreach ($prices as $price) { + $pricesBySku[$price->getSku()][] = $price; + } + + foreach ($prices as $key => $price) { + $this->checkSku($price, $key, $skuDiff, $validationResult); + $this->checkPrice($price, $key, $validationResult); + $ids = isset($idsBySku[$price->getSku()]) ? $idsBySku[$price->getSku()] : []; + $this->checkPriceType($price, $ids, $key, $validationResult); + $this->checkQuantity($price, $key, $validationResult); + $this->checkWebsite($price, $key, $validationResult); + if (isset($pricesBySku[$price->getSku()])) { + $this->checkUnique($price, $pricesBySku, $key, $validationResult); + } + $this->checkUnique($price, $existingPrices, $key, $validationResult); + $this->checkGroup($price, $key, $validationResult); + } + + return $validationResult; + } + + /** + * Check that sku value is correct. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface $price + * @param int $key + * @param array $invalidSkus + * @param Result $validationResult + * @return void + */ + private function checkSku( + \Magento\Catalog\Api\Data\TierPriceInterface $price, + $key, + array $invalidSkus, + Result $validationResult + ) { + if (!$price->getSku() || in_array($price->getSku(), $invalidSkus)) { + $validationResult->addFailedItem( + $key, + __( + 'Invalid attribute SKU = %SKU. ' + . 'Row ID: SKU = %SKU, Website ID: %websiteId, Customer Group: %customerGroup, Quantity: %qty.', + [ + 'SKU' => '%SKU', + 'websiteId' => '%websiteId', + 'customerGroup' => '%customerGroup', + 'qty' => '%qty' + ] + ), + [ + 'SKU' => $price->getSku(), + 'websiteId' => $price->getWebsiteId(), + 'customerGroup' => $price->getCustomerGroup(), + 'qty' => $price->getQuantity() + ] + ); + } + } + + /** + * Verify that price value is correct. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface $price + * @param int $key + * @param Result $validationResult + * @return void + */ + private function checkPrice(\Magento\Catalog\Api\Data\TierPriceInterface $price, $key, Result $validationResult) + { + if ( + null === $price->getPrice() + || $price->getPrice() < 0 + || ($price->getPriceType() === \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT + && $price->getPrice() > 100 + ) + ) { + $validationResult->addFailedItem( + $key, + __( + 'Invalid attribute Price = %price. ' + . 'Row ID: SKU = %SKU, Website ID: %websiteId, Customer Group: %customerGroup, Quantity: %qty.', + [ + 'price' => '%price', + 'SKU' => '%SKU', + 'websiteId' => '%websiteId', + 'customerGroup' => '%customerGroup', + 'qty' => '%qty' + ] + ), + [ + 'price' => $price->getPrice(), + 'SKU' => $price->getSku(), + 'websiteId' => $price->getWebsiteId(), + 'customerGroup' => $price->getCustomerGroup(), + 'qty' => $price->getQuantity() + ] + ); + } + } + + /** + * Verify that price type is correct. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface $price + * @param array $ids + * @param int $key + * @param Result $validationResult + * @return void + */ + private function checkPriceType( + \Magento\Catalog\Api\Data\TierPriceInterface $price, + array $ids, + $key, + Result $validationResult + ) { + if ( + !in_array( + $price->getPriceType(), + [ + \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED, + \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT + ] + ) + || (array_search(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE, $ids) + && $price->getPriceType() !== \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT) + ) { + $validationResult->addFailedItem( + $key, + __( + 'Invalid attribute Price Type = %priceType. ' + . 'Row ID: SKU = %SKU, Website ID: %websiteId, Customer Group: %customerGroup, Quantity: %qty.', + [ + 'price' => '%price', + 'SKU' => '%SKU', + 'websiteId' => '%websiteId', + 'customerGroup' => '%customerGroup', + 'qty' => '%qty' + ] + ), + [ + 'priceType' => $price->getPriceType(), + 'SKU' => $price->getSku(), + 'websiteId' => $price->getWebsiteId(), + 'customerGroup' => $price->getCustomerGroup(), + 'qty' => $price->getQuantity() + ] + ); + } + } + + /** + * Verify that product quantity is correct. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface $price + * @param int $key + * @param Result $validationResult + * @return void + */ + private function checkQuantity(\Magento\Catalog\Api\Data\TierPriceInterface $price, $key, Result $validationResult) + { + if ($price->getQuantity() < 1) { + $validationResult->addFailedItem( + $key, + __( + 'Invalid attribute Quantity = %qty. ' + . 'Row ID: SKU = %SKU, Website ID: %websiteId, Customer Group: %customerGroup, Quantity: %qty.', + [ + 'SKU' => '%SKU', + 'websiteId' => '%websiteId', + 'customerGroup' => '%customerGroup', + 'qty' => '%qty' + ] + ), + [ + 'SKU' => $price->getSku(), + 'websiteId' => $price->getWebsiteId(), + 'customerGroup' => $price->getCustomerGroup(), + 'qty' => $price->getQuantity() + ] + ); + } + } + + /** + * Verify that website exists. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface $price + * @param int $key + * @param Result $validationResult + * @return void + */ + private function checkWebsite(\Magento\Catalog\Api\Data\TierPriceInterface $price, $key, Result $validationResult) + { + try { + $this->websiteRepository->getById($price->getWebsiteId()); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + $validationResult->addFailedItem( + $key, + __( + 'Invalid attribute Website ID = %websiteId. ' + . 'Row ID: SKU = %SKU, Website ID: %websiteId, Customer Group: %customerGroup, Quantity: %qty.', + [ + 'SKU' => '%SKU', + 'websiteId' => '%websiteId', + 'customerGroup' => '%customerGroup', + 'qty' => '%qty' + ] + ), + [ + 'SKU' => $price->getSku(), + 'websiteId' => $price->getWebsiteId(), + 'customerGroup' => $price->getCustomerGroup(), + 'qty' => $price->getQuantity() + ] + ); + } + } + + /** + * Check website value is unique. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface $tierPrice + * @param array $prices + * @param int $key + * @param Result $validationResult + * @return void + */ + private function checkUnique( + \Magento\Catalog\Api\Data\TierPriceInterface $tierPrice, + array $prices, + $key, + Result $validationResult + ) { + if (isset($prices[$tierPrice->getSku()])) { + foreach ($prices[$tierPrice->getSku()] as $price) { + if (strtolower($price->getCustomerGroup()) === strtolower($tierPrice->getCustomerGroup()) + && $price->getQuantity() == $tierPrice->getQuantity() + && ( + ($price->getWebsiteId() == $this->allWebsitesValue + || $tierPrice->getWebsiteId() == $this->allWebsitesValue) + && $price->getWebsiteId() != $tierPrice->getWebsiteId() + ) + ) { + $validationResult->addFailedItem( + $key, + __( + 'We found a duplicate website, tier price, customer group and quantity: ' + . 'Customer Group = %customerGroup, Website ID = %websiteId, Quantity = %qty. ' + . 'Row ID: SKU = %SKU, Website ID: %websiteId, ' + . 'Customer Group: %customerGroup, Quantity: %qty.', + [ + 'SKU' => '%SKU', + 'websiteId' => '%websiteId', + 'customerGroup' => '%customerGroup', + 'qty' => '%qty' + ] + ), + [ + 'SKU' => $price->getSku(), + 'websiteId' => $price->getWebsiteId(), + 'customerGroup' => $price->getCustomerGroup(), + 'qty' => $price->getQuantity() + ] + ); + } + } + } + } + + /** + * Check customer group exists and has correct value. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface $price + * @param int $key + * @param Result $validationResult + * @return void + */ + private function checkGroup(\Magento\Catalog\Api\Data\TierPriceInterface $price, $key, Result $validationResult) + { + $customerGroup = strtolower($price->getCustomerGroup()); + + if ($customerGroup != $this->allGroupsValue && false === $this->retrieveGroupValue($customerGroup)) { + $validationResult->addFailedItem( + $key, + __( + 'No such entity with Customer Group = %customerGroup. ' + . 'Row ID: SKU = %SKU, Website ID: %websiteId, Customer Group: %customerGroup, Quantity: %qty.', + [ + 'SKU' => '%SKU', + 'websiteId' => '%websiteId', + 'customerGroup' => '%customerGroup', + 'qty' => '%qty' + ] + ), + [ + 'SKU' => $price->getSku(), + 'websiteId' => $price->getWebsiteId(), + 'customerGroup' => $price->getCustomerGroup(), + 'qty' => $price->getQuantity() + ] + ); + } + } + + /** + * Retrieve customer group id by code. + * + * @param string $code + * @return int|bool + */ + private function retrieveGroupValue($code) + { + if (!isset($this->customerGroupsByCode[$code])) { + $searchCriteria = $this->searchCriteriaBuilder->addFilters( + [ + $this->filterBuilder->setField('customer_group_code')->setValue($code)->create() + ] + ); + $items = $this->customerGroupRepository->getList($searchCriteria->create())->getItems(); + $item = array_shift($items); + + if (!$item) { + return false; + } + + $this->customerGroupsByCode[strtolower($item->getCode())] = $item->getId(); + } + + return $this->customerGroupsByCode[$code]; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/PriceModifier.php b/app/code/Magento/Catalog/Model/Product/PriceModifier.php index 39d66054db014..2256ff3ae8d64 100644 --- a/app/code/Magento/Catalog/Model/Product/PriceModifier.php +++ b/app/code/Magento/Catalog/Model/Product/PriceModifier.php @@ -1,6 +1,6 @@ getCanShowPrice() !== false; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolverInterface.php b/app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolverInterface.php new file mode 100644 index 0000000000000..f019d5621e02b --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolverInterface.php @@ -0,0 +1,21 @@ +_catalogProductOption = $catalogProductOption; $this->_eavConfig = $eavConfig; @@ -195,6 +204,8 @@ public function __construct( $this->_filesystem = $filesystem; $this->_logger = $logger; $this->productRepository = $productRepository; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** @@ -394,8 +405,7 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p $product->prepareCustomOptions(); $buyRequest->unsetData('_processing_params'); // One-time params only - $product->addCustomOption('info_buyRequest', serialize($buyRequest->getData())); - + $product->addCustomOption('info_buyRequest', $this->serializer->serialize($buyRequest->getData())); if ($options) { $optionIds = array_keys($options); $product->addCustomOption('option_ids', implode(',', $optionIds)); @@ -645,7 +655,7 @@ public function getOrderOptions($product) $optionArr = []; $info = $product->getCustomOption('info_buyRequest'); if ($info) { - $optionArr['info_buyRequest'] = unserialize($info->getValue()); + $optionArr['info_buyRequest'] = $this->serializer->unserialize($info->getValue()); } $optionIds = $product->getCustomOption('option_ids'); @@ -1092,4 +1102,15 @@ public function getAssociatedProducts($product) { return []; } + + /** + * Check if product can be potentially buyed from the category page or some other list + * + * @param \Magento\Catalog\Model\Product $product + * @return bool + */ + public function isPossibleBuyFromList($product) + { + return !$this->hasRequiredOptions($product); + } } diff --git a/app/code/Magento/Catalog/Model/Product/Type/Pool.php b/app/code/Magento/Catalog/Model/Product/Type/Pool.php index 84058a2fed4d5..0139c3cdae0fc 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/Pool.php +++ b/app/code/Magento/Catalog/Model/Product/Type/Pool.php @@ -1,6 +1,6 @@ getExtensionAttributes(); - $websiteId = $extensionAttributes && $extensionAttributes->getWebsiteId() - ? $extensionAttributes->getWebsiteId() - : $websiteId; + $priceWebsiteId = $websiteId; + if (isset($extensionAttributes) && is_numeric($extensionAttributes->getWebsiteId())) { + $priceWebsiteId = (string)$extensionAttributes->getWebsiteId(); + } $prices[] = [ - 'website_id' => $websiteId, + 'website_id' => $priceWebsiteId, 'cust_group' => $price->getCustomerGroupId(), 'website_price' => $price->getValue(), 'price' => $price->getValue(), diff --git a/app/code/Magento/Catalog/Model/Product/Type/Price/Factory.php b/app/code/Magento/Catalog/Model/Product/Type/Price/Factory.php index 257b0e8d0529d..6d1fc78798c99 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/Price/Factory.php +++ b/app/code/Magento/Catalog/Model/Product/Type/Price/Factory.php @@ -1,6 +1,6 @@ metadataPool = $metadataPool; + $this->collectionFactory = $collectionFactory; + $this->idsLimit = (int)$idsLimit; + } + + /** + * {@inheritdoc} + */ + public function retrieveProductIdsBySkus(array $skus) + { + $neededSkus = []; + foreach ($skus as $sku) { + $unifiedSku = strtolower(trim($sku)); + if (!isset($this->idsBySku[$unifiedSku])) { + $neededSkus[] = $sku; + } + } + + if (!empty($neededSkus)) { + /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */ + $collection = $this->collectionFactory->create(); + $collection->addFieldToFilter(\Magento\Catalog\Api\Data\ProductInterface::SKU, ['in' => $neededSkus]); + $linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + + foreach ($collection as $item) { + $this->idsBySku[strtolower(trim($item->getSku()))][$item->getData($linkField)] = $item->getTypeId(); + } + } + + $productIds = []; + foreach ($skus as $sku) { + $unifiedSku = strtolower(trim($sku)); + if (isset($this->idsBySku[$unifiedSku])) { + $productIds[$sku] = $this->idsBySku[$unifiedSku]; + } + } + $this->truncateToLimit(); + return $productIds; + } + + /** + * Cleanup IDs by SKU cache more than some limit. + * + * @return void + */ + private function truncateToLimit() + { + if (count($this->idsBySku) > $this->idsLimit) { + $this->idsBySku = array_slice($this->idsBySku, round($this->idsLimit / -2)); + } + } +} diff --git a/app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php b/app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php new file mode 100644 index 0000000000000..9016dc07d9be1 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php @@ -0,0 +1,21 @@ +providers[$type]->getLinkedProducts($product); $converter = $this->converterPool->getConverter($type); $output = []; + $sorterItems = []; foreach ($products as $item) { $output[$item->getId()] = $converter->convert($item); } - return $output; + + foreach ($output as $item) { + $itemPosition = $item['position']; + if (!isset($sorterItems[$itemPosition])) { + $sorterItems[$itemPosition] = $item; + } else { + $newPosition = $itemPosition + 1; + $sorterItems[$newPosition] = $item; + } + } + ksort($sorterItems); + return $sorterItems; } } diff --git a/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider/Crosssell.php b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider/Crosssell.php index f1955166901a5..4140e18fb13d6 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider/Crosssell.php +++ b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider/Crosssell.php @@ -1,6 +1,6 @@ productFactory->create(); if ($this->storeManager->hasSingleStore()) { @@ -313,15 +314,22 @@ protected function initializeProductData(array $productData, $createNew) */ private function assignProductToWebsites(\Magento\Catalog\Model\Product $product) { + $websiteIds = $product->getWebsiteIds(); + if (!$this->storeManager->hasSingleStore()) { - if ($this->storeManager->getStore()->getCode() == \Magento\Store\Model\Store::ADMIN_CODE) { - $websiteIds = array_keys($this->storeManager->getWebsites()); - } else { - $websiteIds = [$this->storeManager->getStore()->getWebsiteId()]; - } + $websiteIds = array_unique( + array_merge( + $websiteIds, + [$this->storeManager->getStore()->getWebsiteId()] + ) + ); + } - $product->setWebsiteIds(array_unique(array_merge($product->getWebsiteIds(), $websiteIds))); + if ($this->storeManager->getStore(true)->getCode() == \Magento\Store\Model\Store::ADMIN_CODE) { + $websiteIds = array_keys($this->storeManager->getWebsites()); } + + $product->setWebsiteIds($websiteIds); } /** @@ -430,8 +438,15 @@ private function processLinks(\Magento\Catalog\Api\Data\ProductInterface $produc } /** - * @param ProductInterface $product - * @param array $mediaGalleryEntries + * Process Media gallery data before save product. + * + * Compare Media Gallery Entries Data with existing Media Gallery + * * If Media entry has not value_id set it as new + * * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag + * * Merge Existing and new media gallery + * + * @param ProductInterface $product contains only existing media gallery items + * @param array $mediaGalleryEntries array which contains all media gallery items * @return $this * @throws InputException * @throws StateException @@ -441,11 +456,10 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE { $existingMediaGallery = $product->getMediaGallery('images'); $newEntries = []; + $entriesById = []; if (!empty($existingMediaGallery)) { - $entriesById = []; foreach ($mediaGalleryEntries as $entry) { - if (isset($entry['id'])) { - $entry['value_id'] = $entry['id']; + if (isset($entry['value_id'])) { $entriesById[$entry['value_id']] = $entry; } else { $newEntries[] = $entry; @@ -454,6 +468,9 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE foreach ($existingMediaGallery as $key => &$existingEntry) { if (isset($entriesById[$existingEntry['value_id']])) { $updatedEntry = $entriesById[$existingEntry['value_id']]; + if ($updatedEntry['file'] === null) { + unset($updatedEntry['file']); + } $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry); } else { //set the removed flag @@ -481,11 +498,18 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE } /** @var ImageContentInterface $contentDataObject */ $contentDataObject = $this->contentFactory->create() - ->setName($newEntry['content'][ImageContentInterface::NAME]) - ->setBase64EncodedData($newEntry['content'][ImageContentInterface::BASE64_ENCODED_DATA]) - ->setType($newEntry['content'][ImageContentInterface::TYPE]); + ->setName($newEntry['content']['data'][ImageContentInterface::NAME]) + ->setBase64EncodedData($newEntry['content']['data'][ImageContentInterface::BASE64_ENCODED_DATA]) + ->setType($newEntry['content']['data'][ImageContentInterface::TYPE]); $newEntry['content'] = $contentDataObject; $this->processNewMediaGalleryEntry($product, $newEntry); + + $finalGallery = $product->getData('media_gallery'); + $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById)); + $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]); + $entriesById[$newEntryId] = $newEntry; + $finalGallery['images'][$newEntryId] = $newEntry; + $product->setData('media_gallery', $finalGallery); } return $this; } @@ -513,8 +537,6 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO $productDataArray = $this->extensibleDataObjectConverter ->toNestedArray($product, [], \Magento\Catalog\Api\Data\ProductInterface::class); $productDataArray = array_replace($productDataArray, $product->getData()); - unset($productDataArray['media_gallery']); - $ignoreLinksFlag = $product->getData('ignore_links_flag'); $productLinks = null; if (!$ignoreLinksFlag && $ignoreLinksFlag !== null) { @@ -524,8 +546,8 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO $product = $this->initializeProductData($productDataArray, empty($existingProduct)); $this->processLinks($product, $productLinks); - if (isset($productDataArray['media_gallery_entries'])) { - $this->processMediaGallery($product, $productDataArray['media_gallery_entries']); + if (isset($productDataArray['media_gallery'])) { + $this->processMediaGallery($product, $productDataArray['media_gallery']['images']); } if (!$product->getOptionsReadonly()) { @@ -633,6 +655,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr $collection->load(); + $collection->addCategoryIds(); $searchResult = $this->searchResultsFactory->create(); $searchResult->setSearchCriteria($searchCriteria); $searchResult->setItems($collection->getItems()); diff --git a/app/code/Magento/Catalog/Model/ProductType.php b/app/code/Magento/Catalog/Model/ProductType.php index 30119223ef74c..c1c0ecc82c4a0 100644 --- a/app/code/Magento/Catalog/Model/ProductType.php +++ b/app/code/Magento/Catalog/Model/ProductType.php @@ -2,7 +2,7 @@ /** * Product type * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model; diff --git a/app/code/Magento/Catalog/Model/ProductTypeList.php b/app/code/Magento/Catalog/Model/ProductTypeList.php index ad36a7736de66..3e9946f43ea95 100644 --- a/app/code/Magento/Catalog/Model/ProductTypeList.php +++ b/app/code/Magento/Catalog/Model/ProductTypeList.php @@ -2,7 +2,7 @@ /** * Product type provider * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model; diff --git a/app/code/Magento/Catalog/Model/ProductTypes/Config.php b/app/code/Magento/Catalog/Model/ProductTypes/Config.php index a80692cfaf945..2db4b59878e0c 100644 --- a/app/code/Magento/Catalog/Model/ProductTypes/Config.php +++ b/app/code/Magento/Catalog/Model/ProductTypes/Config.php @@ -1,23 +1,32 @@ productRepository->get($productWebsiteLink->getSku()); $product->setWebsiteIds(array_merge($product->getWebsiteIds(), [$productWebsiteLink->getWebsiteId()])); try { - $product->save(); + $this->productRepository->save($product); } catch (\Exception $e) { throw new CouldNotSaveException( __( @@ -68,7 +68,7 @@ public function deleteById($sku, $websiteId) $product->setWebsiteIds(array_diff($product->getWebsiteIds(), [$websiteId])); try { - $product->save(); + $this->productRepository->save($product); } catch (\Exception $e) { throw new CouldNotSaveException( __( diff --git a/app/code/Magento/Catalog/Model/ResourceModel/AbstractCollection.php b/app/code/Magento/Catalog/Model/ResourceModel/AbstractCollection.php index e026d6540aceb..05e8e0a14e8b5 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/AbstractCollection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/AbstractCollection.php @@ -1,6 +1,6 @@ getChildrenCount()) { $object->setChildrenCount(0); } - + $object->setAttributeSetId( + $object->getAttributeSetId() ?: $this->getEntityType()->getDefaultAttributeSetId() + ); if ($object->isObjectNew()) { if ($object->getPosition() === null) { $object->setPosition($this->_getMaxPosition($object->getPath()) + 1); @@ -573,22 +580,29 @@ public function getIsActiveAttributeId() */ public function findWhereAttributeIs($entityIdsFilter, $attribute, $expectedValue) { - $linkField = $this->getLinkField(); - $bind = ['attribute_id' => $attribute->getId(), 'value' => $expectedValue]; - $selectEntities = $this->getConnection()->select()->from( - ['ce' => $this->getTable('catalog_category_entity')], - ['entity_id'] - )->joinLeft( - ['ci' => $attribute->getBackend()->getTable()], - "ci.{$linkField} = ce.{$linkField} AND attribute_id = :attribute_id", - ['value'] - )->where( - 'ci.value = :value' - )->where( - 'ce.entity_id IN (?)', - $entityIdsFilter - ); - return $this->getConnection()->fetchCol($selectEntities, $bind); + $entityIdsFilterHash = md5(serialize($entityIdsFilter)); + + if (!isset($this->entitiesWhereAttributesIs[$entityIdsFilterHash][$attribute->getId()][$expectedValue])) { + $linkField = $this->getLinkField(); + $bind = ['attribute_id' => $attribute->getId(), 'value' => $expectedValue]; + $selectEntities = $this->getConnection()->select()->from( + ['ce' => $this->getTable('catalog_category_entity')], + ['entity_id'] + )->joinLeft( + ['ci' => $attribute->getBackend()->getTable()], + "ci.{$linkField} = ce.{$linkField} AND attribute_id = :attribute_id", + ['value'] + )->where( + 'ci.value = :value' + )->where( + 'ce.entity_id IN (?)', + $entityIdsFilter + ); + $this->entitiesWhereAttributesIs[$entityIdsFilterHash][$attribute->getId()][$expectedValue] = + $this->getConnection()->fetchCol($selectEntities, $bind); + } + + return $this->entitiesWhereAttributesIs[$entityIdsFilterHash][$attribute->getId()][$expectedValue]; } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php index 4599fc4a8428a..7d55ebc3e9f91 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php @@ -1,6 +1,6 @@ getData(self::APPLY_TO)) { - if (is_array($this->getData(self::APPLY_TO))) { - return $this->getData(self::APPLY_TO); - } - return explode(',', $this->getData(self::APPLY_TO)); - } else { - return []; + $applyTo = $this->_getData(self::APPLY_TO) ?: []; + if (!is_array($applyTo)) { + $applyTo = explode(',', $applyTo); } + return $applyTo; } /** @@ -420,6 +417,9 @@ public function isIndexable() if ($this->getAttributeCode() == 'price') { return false; } + if ($this->getAttributeCode() == 'visibility') { + return true; + } if (!$this->getIsFilterableInSearch() && !$this->getIsVisibleInAdvancedSearch() && !$this->getIsFilterable()) { return false; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Helper.php b/app/code/Magento/Catalog/Model/ResourceModel/Helper.php index 36dbf0b9739c7..d9490b418c37c 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Helper.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Helper.php @@ -1,6 +1,6 @@ productCategoryLink; } + + /** + * Extends parent method to be appropriate for product. + * Store id is required to correctly identify attribute value we are working with. + * + * {@inheritdoc} + */ + protected function getAttributeRow($entity, $object, $attribute) + { + $data = parent::getAttributeRow($entity, $object, $attribute); + $data['store_id'] = $object->getStoreId(); + return $data; + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php index 7075e1e11d493..46ecffcb6f442 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Action.php @@ -1,6 +1,6 @@ moduleManager = $moduleManager; $this->_catalogProductFlatState = $catalogProductFlatState; @@ -316,7 +323,11 @@ public function __construct( $this->_resourceHelper = $resourceHelper; $this->dateTime = $dateTime; $this->_groupManagement = $groupManagement; - $this->_productLimitationFilters = $this->createLimitationFilters(); + $productLimitationFactory = $productLimitationFactory ?: ObjectManager::getInstance()->get( + \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory::class + ); + $this->_productLimitationFilters = $productLimitationFactory->create(); + $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class); parent::__construct( $entityFactory, $logger, @@ -2181,14 +2192,25 @@ public function addMediaGalleryData() ); $mediaGalleries = []; - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); - + $linkField = $this->getProductEntityMetadata()->getLinkField(); + $items = $this->getItems(); + + $select->where( + 'entity.' . $linkField . ' IN (?)', + array_map( + function ($item) use ($linkField) { + return $item->getData($linkField); + }, + $items + ) + ); foreach ($this->getConnection()->fetchAll($select) as $row) { $mediaGalleries[$row[$linkField]][] = $row; } - foreach ($this->getItems() as $item) { - $mediaEntries = isset($mediaGalleries[$item->getId()]) ? $mediaGalleries[$item->getId()] : []; + foreach ($items as $item) { + $mediaEntries = isset($mediaGalleries[$item->getData($linkField)]) ? + $mediaGalleries[$item->getData($linkField)] : []; $this->getGalleryReadHandler()->addMediaDataToProduct($item, $mediaEntries); } @@ -2197,15 +2219,13 @@ public function addMediaGalleryData() } /** - * Get MetadataPool instance - * @return MetadataPool + * Get product entity metadata + * + * @return \Magento\Framework\EntityManager\EntityMetadataInterface */ - private function getMetadataPool() + public function getProductEntityMetadata() { - if (!$this->metadataPool) { - $this->metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - } - return $this->metadataPool; + return $this->metadataPool->getMetadata(ProductInterface::class); } /** @@ -2329,13 +2349,4 @@ public function getPricesCount() return $this->_pricesCount; } - - /** - * @return Collection\ProductLimitation - */ - private function createLimitationFilters() - { - return \Magento\Framework\App\ObjectManager::getInstance() - ->create(\Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class); - } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection/ProductLimitation.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection/ProductLimitation.php index aba330bea0651..28fcb4ab5c405 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection/ProductLimitation.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection/ProductLimitation.php @@ -1,6 +1,6 @@ baseSelectProcessors = $baseSelectProcessors; + } + + /** + * @param Select $select + * @return Select + */ + public function process(Select $select) + { + foreach ($this->baseSelectProcessors as $baseSelectProcessor) { + $select = $baseSelectProcessor->process($select); + } + return $select; + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Flat.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Flat.php index 56f14d4c12577..2dd445377d6ea 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Flat.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Flat.php @@ -1,6 +1,6 @@ getConnection()->fetchAll($select); } + + /** + * Counts uses of this image. + * + * @param string $image + * @return int + */ + public function countImageUses($image) + { + $select = $this->getConnection()->select() + ->from([$this->getMainTableAlias() => $this->getMainTable()]) + ->where( + 'value = ?', + $image + ); + return count($this->getConnection()->fetchAll($select)); + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/AbstractIndexer.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/AbstractIndexer.php index 44d9b1c0304a8..378a3ddf099ab 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/AbstractIndexer.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/AbstractIndexer.php @@ -1,6 +1,6 @@ joinLeft( ['e' => $this->getTable('catalog_product_entity')], 'e.' . $linkField .' = l.parent_id', - ['e.entity_id as parent_id'] + [] )->join( ['cs' => $this->getTable('store')], '', @@ -205,9 +205,17 @@ protected function _prepareRelationIndexSelect($parentIds = null) )->join( ['i' => $idxTable], 'l.child_id = i.entity_id AND cs.store_id = i.store_id', - ['attribute_id', 'store_id', 'value'] + [] )->group( - ['parent_id', 'i.attribute_id', 'i.store_id', 'i.value'] + ['parent_id', 'i.attribute_id', 'i.store_id', 'i.value', 'l.child_id'] + )->columns( + [ + 'parent_id' => 'e.entity_id', + 'attribute_id' => 'i.attribute_id', + 'store_id' => 'i.store_id', + 'value' => 'i.value', + 'source_id' => 'l.child_id' + ] ); if ($parentIds !== null) { $select->where('e.entity_id IN(?)', $parentIds); @@ -222,7 +230,7 @@ protected function _prepareRelationIndexSelect($parentIds = null) 'select' => $select, 'entity_field' => new \Zend_Db_Expr('l.parent_id'), 'website_field' => new \Zend_Db_Expr('cs.website_id'), - 'store_field' => new \Zend_Db_Expr('cs.store_id') + 'store_field' => new \Zend_Db_Expr('cs.store_id'), ] ); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Decimal.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Decimal.php index e8d9889e68d59..76127b02d5fb9 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Decimal.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Decimal.php @@ -1,6 +1,6 @@ $productValueExpression, + 'source_id' => 'cpe.entity_id', ] ); @@ -116,7 +117,7 @@ protected function _prepareIndex($entityIds = null, $attributeId = null) 'select' => $select, 'entity_field' => new \Zend_Db_Expr('cpe.entity_id'), 'website_field' => new \Zend_Db_Expr('cs.website_id'), - 'store_field' => new \Zend_Db_Expr('cs.store_id') + 'store_field' => new \Zend_Db_Expr('cs.store_id'), ] ); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php index c4eda1c987192..7a698ae595d19 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php @@ -1,6 +1,6 @@ $ifNullSql, + 'pid.entity_id', ] )->where( 'pid.attribute_id IN(?)', @@ -200,7 +201,7 @@ protected function _prepareSelectIndex($entityIds = null, $attributeId = null) 'select' => $select, 'entity_field' => new \Zend_Db_Expr('pid.entity_id'), 'website_field' => new \Zend_Db_Expr('pid.website_id'), - 'store_field' => new \Zend_Db_Expr('pid.store_id') + 'store_field' => new \Zend_Db_Expr('pid.store_id'), ] ); $query = $select->insertFromSelect($idxTable); @@ -221,11 +222,7 @@ protected function _prepareMultiselectIndex($entityIds = null, $attributeId = nu $connection = $this->getConnection(); // prepare multiselect attributes - if ($attributeId === null) { - $attrIds = $this->_getIndexableAttributes(true); - } else { - $attrIds = [$attributeId]; - } + $attrIds = $attributeId === null ? $this->_getIndexableAttributes(true) : [$attributeId]; if (!$attrIds) { return $this; @@ -247,20 +244,20 @@ protected function _prepareMultiselectIndex($entityIds = null, $attributeId = nu $productValueExpression = $connection->getCheckSql('pvs.value_id > 0', 'pvs.value', 'pvd.value'); $select = $connection->select()->from( ['pvd' => $this->getTable('catalog_product_entity_varchar')], - [$productIdField, 'attribute_id'] + [] )->join( ['cs' => $this->getTable('store')], '', - ['store_id'] + [] )->joinLeft( ['pvs' => $this->getTable('catalog_product_entity_varchar')], "pvs.{$productIdField} = pvd.{$productIdField} AND pvs.attribute_id = pvd.attribute_id" . ' AND pvs.store_id=cs.store_id', - ['value' => $productValueExpression] + [] )->joinLeft( ['cpe' => $this->getTable('catalog_product_entity')], "cpe.{$productIdField} = pvd.{$productIdField}", - ['entity_id'] + [] )->where( 'pvd.store_id=?', $connection->getIfNullSql('pvs.store_id', \Magento\Store\Model\Store::DEFAULT_STORE_ID) @@ -272,6 +269,14 @@ protected function _prepareMultiselectIndex($entityIds = null, $attributeId = nu $attrIds )->where( 'cpe.entity_id IS NOT NULL' + )->columns( + [ + 'entity_id' => 'cpe.entity_id', + 'attribute_id' => 'attribute_id', + 'store_id' => 'cs.store_id', + 'value' => $productValueExpression, + 'source_id' => 'cpe.entity_id', + ] ); $statusCond = $connection->quoteInto('=?', ProductStatus::STATUS_ENABLED); @@ -289,30 +294,11 @@ protected function _prepareMultiselectIndex($entityIds = null, $attributeId = nu 'select' => $select, 'entity_field' => new \Zend_Db_Expr('cpe.entity_id'), 'website_field' => new \Zend_Db_Expr('cs.website_id'), - 'store_field' => new \Zend_Db_Expr('cs.store_id') + 'store_field' => new \Zend_Db_Expr('cs.store_id'), ] ); - $i = 0; - $data = []; - $query = $select->query(); - while ($row = $query->fetch()) { - $values = explode(',', $row['value']); - foreach ($values as $valueId) { - if (isset($options[$row['attribute_id']][$valueId])) { - $data[] = [$row['entity_id'], $row['attribute_id'], $row['store_id'], $valueId]; - $i++; - if ($i % 10000 == 0) { - $this->_saveIndexData($data); - $data = []; - } - } - } - } - - $this->_saveIndexData($data); - unset($options); - unset($data); + $this->saveDataFromSelect($select, $options); return $this; } @@ -331,7 +317,7 @@ protected function _saveIndexData(array $data) $connection = $this->getConnection(); $connection->insertArray( $this->getIdxTable(), - ['entity_id', 'attribute_id', 'store_id', 'value'], + ['entity_id', 'attribute_id', 'store_id', 'value', 'source_id'], $data ); return $this; @@ -348,4 +334,31 @@ public function getIdxTable($table = null) { return $this->tableStrategy->getTableName('catalog_product_index_eav'); } + + /** + * @param \Magento\Framework\DB\Select $select + * @param array $options + * @return void + */ + private function saveDataFromSelect(\Magento\Framework\DB\Select $select, array $options) + { + $i = 0; + $data = []; + $query = $select->query(); + while ($row = $query->fetch()) { + $values = explode(',', $row['value']); + foreach ($values as $valueId) { + if (isset($options[$row['attribute_id']][$valueId])) { + $data[] = [$row['entity_id'], $row['attribute_id'], $row['store_id'], $valueId, $row['source_id']]; + $i++; + if ($i % 10000 == 0) { + $this->_saveIndexData($data); + $data = []; + } + } + } + } + + $this->_saveIndexData($data); + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php index ea72691ea0039..ffaf8a5d100d3 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php @@ -1,12 +1,13 @@ storeManager = $storeManager; $this->resource = $resourceConnection; $this->customerSession = $customerSession; $this->metadataPool = $metadataPool; + $this->baseSelectProcessor = (null !== $baseSelectProcessor) + ? $baseSelectProcessor : ObjectManager::getInstance()->get(BaseSelectProcessorInterface::class); } /** @@ -58,24 +68,27 @@ public function build($productId) $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $productTable = $this->resource->getTableName('catalog_product_entity'); - return [$this->resource->getConnection()->select() + $priceSelect = $this->resource->getConnection()->select() ->from(['parent' => $productTable], '') ->joinInner( ['link' => $this->resource->getTableName('catalog_product_relation')], "link.parent_id = parent.$linkField", [] )->joinInner( - ['child' => $productTable], - "child.entity_id = link.child_id", + [BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS => $productTable], + sprintf('%s.entity_id = link.child_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), ['entity_id'] )->joinInner( ['t' => $this->resource->getTableName('catalog_product_index_price')], - 't.entity_id = child.entity_id', + sprintf('t.entity_id = %s.entity_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), [] - )->where('parent.entity_id = ? ', $productId) + )->where('parent.entity_id = ?', $productId) ->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId()) ->where('t.customer_group_id = ?', $this->customerSession->getCustomerGroupId()) ->order('t.min_price ' . Select::SQL_ASC) - ->limit(1)]; + ->limit(1); + $priceSelect = $this->baseSelectProcessor->process($priceSelect); + + return [$priceSelect]; } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php index 289445ae2daf0..8a97d43a18d7e 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php @@ -1,6 +1,6 @@ _prepareDefaultFinalPriceTable(); + + $select = $this->getSelect($entityIds, $type); + $query = $select->insertFromSelect($this->_getDefaultFinalPriceTable(), [], false); + $this->getConnection()->query($query); + return $this; + } + + /** + * Forms Select for collecting price related data for final price index table + * Next types of prices took into account: default, special, tier price + * Moved to protected for possible reusing + * + * @param int|array $entityIds Ids for filtering output result + * @param string|null $type Type for filtering output result by specified product type (all if null) + * @return \Magento\Framework\DB\Select + * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + protected function getSelect($entityIds = null, $type = null) + { $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); $connection = $this->getConnection(); $select = $connection->select()->from( @@ -368,13 +387,10 @@ protected function prepareFinalPriceDataForType($entityIds, $type) 'select' => $select, 'entity_field' => new \Zend_Db_Expr('e.entity_id'), 'website_field' => new \Zend_Db_Expr('cw.website_id'), - 'store_field' => new \Zend_Db_Expr('cs.store_id') + 'store_field' => new \Zend_Db_Expr('cs.store_id'), ] ); - - $query = $select->insertFromSelect($this->_getDefaultFinalPriceTable(), [], false); - $connection->query($query); - return $this; + return $select; } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Factory.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Factory.php index ddaf7adef8c14..bacf79408ca6b 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Factory.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Factory.php @@ -1,6 +1,6 @@ fetchOne($select, $bind); } + /** + * Check if product has links. + * + * @param int $parentId ID of product + * @return bool + */ + public function hasProductLinks($parentId) + { + $connection = $this->getConnection(); + $select = $connection->select()->from( + $this->getMainTable(), + ['count' => new \Zend_Db_Expr('COUNT(*)')] + )->where( + 'product_id = :product_id' + ) ; + + return $connection->fetchOne( + $select, + [ + 'product_id' => $parentId + ] + ) > 0; + } + /** * Save Product Links process * diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/Collection.php index 999d70d808102..da9a46c9e106d 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/Collection.php @@ -1,6 +1,6 @@ getMetadataPool()->getMetadata(ProductInterface::class)->getIdentifierField(); + $identifierField = $this->getProductEntityMetadata()->getIdentifierField(); $this->getSelect()->where("product_entity_table.$identifierField IN (?)", $products); $this->_hasLinkFilter = true; } @@ -279,7 +200,7 @@ protected function _joinLinks() $connection->quoteInto('links.link_type_id = ?', $this->_linkTypeId), ]; $joinType = 'join'; - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $linkField = $this->getProductEntityMetadata()->getLinkField(); if ($this->getProduct() && $this->getProduct()->getId()) { $linkFieldId = $this->getProduct()->getData( $linkField @@ -422,19 +343,6 @@ public function addLinkAttributeToFilter($code, $condition) return $this; } - /** - * Get MetadataPool instance - * @return MetadataPool - * @deprecated - */ - private function getMetadataPool() - { - if (!$this->metadataPool) { - $this->metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - } - return $this->metadataPool; - } - /** * Join Product To Links * @return void @@ -442,11 +350,15 @@ private function getMetadataPool() private function joinProductsToLinks() { if ($this->_hasLinkFilter) { - $metaDataPool = $this->getMetadataPool()->getMetadata(ProductInterface::class); + $metaDataPool = $this->getProductEntityMetadata(); $linkField = $metaDataPool->getLinkField(); $entityTable = $metaDataPool->getEntityTable(); $this->getSelect() - ->join(['product_entity_table' => $entityTable], "links.product_id = product_entity_table.$linkField", []); + ->join( + ['product_entity_table' => $entityTable], + "links.product_id = product_entity_table.$linkField", + [] + ); } } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/SaveHandler.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/SaveHandler.php index ad987c6c8b4b3..24a7210d8ff12 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/SaveHandler.php @@ -1,6 +1,6 @@ storeManager = $storeManager; $this->resource = $resourceConnection; $this->eavConfig = $eavConfig; $this->catalogHelper = $catalogHelper; $this->metadataPool = $metadataPool; + $this->baseSelectProcessor = (null !== $baseSelectProcessor) + ? $baseSelectProcessor : ObjectManager::getInstance()->get(BaseSelectProcessorInterface::class); } /** @@ -74,28 +84,29 @@ public function build($productId) "link.parent_id = parent.$linkField", [] )->joinInner( - ['child' => $productTable], - "child.entity_id = link.child_id", + [BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS => $productTable], + sprintf('%s.entity_id = link.child_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS, $linkField), ['entity_id'] )->joinInner( ['t' => $priceAttribute->getBackendTable()], - "t.$linkField = child.$linkField", + sprintf('t.%s = %s.%1$s', $linkField, BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), [] - )->where('parent.entity_id = ? ', $productId) + )->where('parent.entity_id = ?', $productId) ->where('t.attribute_id = ?', $priceAttribute->getAttributeId()) ->where('t.value IS NOT NULL') ->order('t.value ' . Select::SQL_ASC) ->limit(1); - - $priceSelectDefault = clone $priceSelect; - $priceSelectDefault->where('t.store_id = ?', Store::DEFAULT_STORE_ID); - $select[] = $priceSelectDefault; + $priceSelect = $this->baseSelectProcessor->process($priceSelect); if (!$this->catalogHelper->isPriceGlobal()) { - $priceSelect->where('t.store_id = ?', $this->storeManager->getStore()->getId()); - $select[] = $priceSelect; + $priceSelectStore = clone $priceSelect; + $priceSelectStore->where('t.store_id = ?', $this->storeManager->getStore()->getId()); + $selects[] = $priceSelectStore; } - return $select; + $priceSelect->where('t.store_id = ?', Store::DEFAULT_STORE_ID); + $selects[] = $priceSelect; + + return $selects; } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php index 792a8f5b86d10..17ce55b1252d6 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php @@ -1,15 +1,19 @@ storeManager = $storeManager; $this->resource = $resourceConnection; @@ -72,6 +83,8 @@ public function __construct( $this->dateTime = $dateTime; $this->localeDate = $localeDate; $this->metadataPool = $metadataPool; + $this->baseSelectProcessor = (null !== $baseSelectProcessor) + ? $baseSelectProcessor : ObjectManager::getInstance()->get(BaseSelectProcessorInterface::class); } /** @@ -95,12 +108,12 @@ public function build($productId) "link.parent_id = parent.$linkField", [] )->joinInner( - ['child' => $productTable], - "child.entity_id = link.child_id", + [BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS => $productTable], + sprintf('%s.entity_id = link.child_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), ['entity_id'] )->joinInner( ['t' => $specialPriceAttribute->getBackendTable()], - "t.$linkField = child.$linkField", + sprintf('t.%s = %s.%1$s', $linkField, BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), [] )->joinLeft( ['special_from' => $specialPriceFromDate->getBackendTable()], @@ -116,7 +129,7 @@ public function build($productId) $specialPriceToDate->getAttributeId() ), '' - )->where('parent.entity_id = ? ', $productId) + )->where('parent.entity_id = ?', $productId) ->where('t.attribute_id = ?', $specialPriceAttribute->getAttributeId()) ->where('t.value IS NOT NULL') ->where( @@ -127,16 +140,17 @@ public function build($productId) $currentDate )->order('t.value ' . Select::SQL_ASC) ->limit(1); - - $specialPriceDefault = clone $specialPrice; - $specialPriceDefault->where('t.store_id = ?', Store::DEFAULT_STORE_ID); - $select[] = $specialPriceDefault; + $specialPrice = $this->baseSelectProcessor->process($specialPrice); if (!$this->catalogHelper->isPriceGlobal()) { - $specialPrice->where('t.store_id = ?', $this->storeManager->getStore()->getId()); - $select[] = $specialPrice; + $priceSelectStore = clone $specialPrice; + $priceSelectStore->where('t.store_id = ?', $this->storeManager->getStore()->getId()); + $selects[] = $priceSelectStore; } - return $select; + $specialPrice->where('t.store_id = ?', Store::DEFAULT_STORE_ID); + $selects[] = $specialPrice; + + return $selects; } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php index d2d6d89c0a2a5..b884d2504a47a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php @@ -1,12 +1,12 @@ storeManager = $storeManager; $this->resource = $resourceConnection; $this->customerSession = $customerSession; $this->catalogHelper = $catalogHelper; $this->metadataPool = $metadataPool; + $this->baseSelectProcessor = (null !== $baseSelectProcessor) + ? $baseSelectProcessor : ObjectManager::getInstance()->get(BaseSelectProcessorInterface::class); } /** @@ -77,28 +86,29 @@ public function build($productId) "link.parent_id = parent.$linkField", [] )->joinInner( - ['child' => $productTable], - "child.entity_id = link.child_id", + [BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS => $productTable], + sprintf('%s.entity_id = link.child_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), ['entity_id'] )->joinInner( ['t' => $this->resource->getTableName('catalog_product_entity_tier_price')], - "t.$linkField = child.$linkField", + sprintf('t.%s = %s.%1$s', $linkField, BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), [] - )->where('parent.entity_id = ? ', $productId) + )->where('parent.entity_id = ?', $productId) ->where('t.all_groups = 1 OR customer_group_id = ?', $this->customerSession->getCustomerGroupId()) ->where('t.qty = ?', 1) ->order('t.value ' . Select::SQL_ASC) ->limit(1); - - $priceSelectDefault = clone $priceSelect; - $priceSelectDefault->where('t.website_id = ?', self::DEFAULT_WEBSITE_ID); - $select[] = $priceSelectDefault; + $priceSelect = $this->baseSelectProcessor->process($priceSelect); if (!$this->catalogHelper->isPriceGlobal()) { - $priceSelect->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId()); - $select[] = $priceSelect; + $priceSelectStore = clone $priceSelect; + $priceSelectStore->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId()); + $selects[] = $priceSelectStore; } - return $select; + $priceSelect->where('t.website_id = ?', self::DEFAULT_WEBSITE_ID); + $selects[] = $priceSelect; + + return $selects; } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php index f3dc225685985..1ba05e55fe2b1 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php @@ -1,6 +1,6 @@ linkedProductSelectBuilder as $productSelectBuilder) { - $select = array_merge($select, $productSelectBuilder->build($productId)); + $selects = array_merge($selects, $productSelectBuilder->build($productId)); } - return $select; + return $selects; } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php index e437791fd3d99..94bdaeaedf552 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php @@ -1,6 +1,6 @@ getTable('catalog_product_option_title'); foreach ([\Magento\Store\Model\Store::DEFAULT_STORE_ID, $object->getStoreId()] as $storeId) { $existInCurrentStore = $this->getColFromOptionTable($titleTableName, (int)$object->getId(), (int)$storeId); - $existInDefaultStore = $this->getColFromOptionTable( - $titleTableName, - (int)$object->getId(), - \Magento\Store\Model\Store::DEFAULT_STORE_ID - ); + $existInDefaultStore = (int)$storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID ? + $existInCurrentStore : + $this->getColFromOptionTable( + $titleTableName, + (int)$object->getId(), + \Magento\Store\Model\Store::DEFAULT_STORE_ID + ); + if ($object->getTitle()) { + $isDeleteStoreTitle = (bool)$object->getData('is_delete_store_title'); if ($existInCurrentStore) { - if ($object->getStoreId() == $storeId) { + if ($isDeleteStoreTitle && (int)$storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID) { + $connection->delete($titleTableName, ['option_title_id = ?' => $existInCurrentStore]); + + } elseif ($object->getStoreId() == $storeId) { $data = $this->_prepareDataForTable( new \Magento\Framework\DataObject(['title' => $object->getTitle()]), $titleTableName @@ -270,8 +277,13 @@ protected function _saveValueTitles(\Magento\Framework\Model\AbstractModel $obje } } else { // we should insert record into not default store only of if it does not exist in default store - if (($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID && !$existInDefaultStore) - || ($storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID && !$existInCurrentStore) + if ( + ($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID && !$existInDefaultStore) || + ( + $storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID && + !$existInCurrentStore && + !$isDeleteStoreTitle + ) ) { $data = $this->_prepareDataForTable( new \Magento\Framework\DataObject( diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Collection.php index 2d50ead9d56d9..8f434995de9e8 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Collection.php @@ -1,12 +1,13 @@ _optionValueCollectionFactory = $optionValueCollectionFactory; $this->_storeManager = $storeManager; + $this->metadataPool = $metadataPool ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\EntityManager\MetadataPool::class); parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource); } @@ -248,7 +253,7 @@ protected function _initSelect() ['cpe' => $this->getTable('catalog_product_entity')], sprintf( 'cpe.%s = main_table.product_id', - $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField() + $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField() ), [] ); @@ -318,18 +323,6 @@ public function reset() return $this->_reset(); } - /** - * @return \Magento\Framework\EntityManager\MetadataPool - */ - private function getMetadataPool() - { - if (null === $this->metadataPool) { - $this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\EntityManager\MetadataPool::class); - } - return $this->metadataPool; - } - /** * @return JoinProcessorInterface */ diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php index 39a67f9722e18..2dad5d0a441ec 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php @@ -1,6 +1,6 @@ getTable('catalog_product_option_type_price'); + $formattedPrice = $this->getLocaleFormatter()->getNumber($object->getPrice()); - $price = (double)sprintf('%F', $object->getPrice()); + $price = (double)sprintf('%F', $formattedPrice); $priceType = $object->getPriceType(); if ($object->getPrice() && $priceType) { @@ -231,6 +237,11 @@ protected function _saveValueTitles(\Magento\Framework\Model\AbstractModel $obje ); $optionTypeId = $this->getConnection()->fetchOne($select); $existInCurrentStore = $this->getOptionIdFromOptionTable($titleTable, (int)$object->getId(), (int)$storeId); + + if ($storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID && $object->getData('is_delete_store_title')) { + $object->unsetData('title'); + } + if ($object->getTitle()) { if ($existInCurrentStore) { if ($storeId == $object->getStoreId()) { @@ -410,4 +421,19 @@ public function duplicate(\Magento\Catalog\Model\Product\Option\Value $object, $ return $object; } + + /** + * Get FormatInterface to convert price from string to number format + * + * @return \Magento\Framework\Locale\FormatInterface + * @deprecated + */ + private function getLocaleFormatter() + { + if ($this->localeFormat === null) { + $this->localeFormat = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Locale\FormatInterface::class); + } + return $this->localeFormat; + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value/Collection.php index 0924808a7dcac..1f65ec334dee4 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value/Collection.php @@ -1,6 +1,6 @@ attributeResource = $attributeResource; + $this->attributeRepository = $attributeRepository; + $this->productIdLocator = $productIdLocator; + $this->metadataPool = $metadataPool; + } + + /** + * {@inheritdoc} + */ + public function get(array $skus) + { + $ids = $this->retrieveAffectedIds($skus); + $priceTable = $this->attributeResource->getTable($this->priceTable); + $dateTimeTable = $this->attributeResource->getTable($this->datetimeTable); + $linkField = $this->getEntityLinkField(); + $select = $this->attributeResource->getConnection() + ->select() + ->from( + $priceTable, + [ + 'value_id', + 'store_id', + $this->getEntityLinkField(), + 'value', + ] + ) + ->joinLeft( + $dateTimeTable . ' AS datetime_from', + $priceTable . '.' . $linkField . '=' . 'datetime_from.' . $linkField + . ' AND datetime_from.attribute_id=' . $this->getPriceFromAttributeId(), + 'value AS price_from' + ) + ->joinLeft( + $dateTimeTable . ' AS datetime_to', + $priceTable . '.' . $linkField . '=' . 'datetime_to.' . $linkField + . ' AND datetime_to.attribute_id=' . $this->getPriceToAttributeId(), + 'value AS price_to' + ) + ->where($priceTable . '.' . $linkField . ' IN (?)', $ids) + ->where($priceTable . '.attribute_id = ?', $this->getPriceAttributeId()); + + return $this->attributeResource->getConnection()->fetchAll($select); + } + + /** + * {@inheritdoc} + */ + public function update(array $prices) + { + $formattedPrices = []; + $formattedDates = []; + + /** @var \Magento\Catalog\Api\Data\SpecialPriceInterface $price */ + foreach ($prices as $price) { + $productIdsBySku = $this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()]); + $ids = array_keys($productIdsBySku[$price->getSku()]); + foreach ($ids as $id) { + $formattedPrices[] = [ + 'store_id' => $price->getStoreId(), + $this->getEntityLinkField() => $id, + 'value' => $price->getPrice(), + 'attribute_id' => $this->getPriceAttributeId(), + ]; + if ($price->getPriceFrom()) { + $formattedDates[] = [ + 'store_id' => $price->getStoreId(), + $this->getEntityLinkField() => $id, + 'value' => $price->getPriceFrom(), + 'attribute_id' => $this->getPriceFromAttributeId(), + ]; + } + if ($price->getPriceTo()) { + $formattedDates[] = [ + 'store_id' => $price->getStoreId(), + $this->getEntityLinkField() => $id, + 'value' => $price->getPriceTo(), + 'attribute_id' => $this->getPriceToAttributeId(), + ]; + } + } + } + $connection = $this->attributeResource->getConnection(); + $connection->beginTransaction(); + + try { + $this->updateItems($formattedPrices, $this->priceTable); + $this->updateItems($formattedDates, $this->datetimeTable); + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotSaveException( + __('Could not save Prices.'), + $e + ); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function delete(array $prices) + { + $skus = array_unique( + array_map(function ($price) { + return $price->getSku(); + }, $prices) + ); + $ids = $this->retrieveAffectedIds($skus); + $connection = $this->attributeResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) { + $this->attributeResource->getConnection()->delete( + $this->attributeResource->getTable($this->priceTable), + [ + 'attribute_id = ?' => $this->getPriceAttributeId(), + $this->getEntityLinkField() . ' IN (?)' => $idsBunch + ] + ); + } + foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) { + $this->attributeResource->getConnection()->delete( + $this->attributeResource->getTable($this->datetimeTable), + [ + 'attribute_id IN (?)' => [$this->getPriceFromAttributeId(), $this->getPriceToAttributeId()], + $this->getEntityLinkField() . ' IN (?)' => $idsBunch + ] + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotDeleteException( + __('Could not delete Prices'), + $e + ); + } + + return true; + } + + /** + * Get link field. + * + * @return string + */ + public function getEntityLinkField() + { + return $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + } + + /** + * Update items in database. + * + * @param array $items + * @param string $table + * @return void + */ + private function updateItems(array $items, $table) + { + foreach (array_chunk($items, $this->itemsPerOperation) as $itemsBunch) { + $this->attributeResource->getConnection()->insertOnDuplicate( + $this->attributeResource->getTable($table), + $itemsBunch, + ['value'] + ); + } + } + + /** + * Get special price attribute ID. + * + * @return int + */ + private function getPriceAttributeId() + { + if (!$this->priceAttributeId) { + $this->priceAttributeId = $this->attributeRepository->get('special_price')->getAttributeId(); + } + + return $this->priceAttributeId; + } + + /** + * Get special price from attribute ID. + * + * @return int + */ + private function getPriceFromAttributeId() + { + if (!$this->priceFromAttributeId) { + $this->priceFromAttributeId = $this->attributeRepository->get('special_from_date')->getAttributeId(); + } + + return $this->priceFromAttributeId; + } + + /** + * Get special price to attribute ID. + * + * @return int + */ + private function getPriceToAttributeId() + { + if (!$this->priceToAttributeId) { + $this->priceToAttributeId = $this->attributeRepository->get('special_to_date')->getAttributeId(); + } + + return $this->priceToAttributeId; + } + + /** + * Retrieve IDs of products, that were affected during price update. + * + * @param array $skus + * @return array + */ + private function retrieveAffectedIds(array $skus) + { + $affectedIds = []; + + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $productIds) { + $affectedIds = array_merge($affectedIds, array_keys($productIds)); + } + + return array_unique($affectedIds); + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Relation.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Relation.php index c057ff91e6944..013adfcb18f4a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Relation.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Relation.php @@ -1,6 +1,6 @@ eavConfig = $eavConfig; + $this->metadataPool = $metadataPool; + $this->storeResolver = $storeResolver; + } + + /** + * @param Select $select + * @return Select + */ + public function process(Select $select) + { + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + $statusAttribute = $this->eavConfig->getAttribute(Product::ENTITY, ProductInterface::STATUS); + + $select->joinLeft( + ['status_global_attr' => $statusAttribute->getBackendTable()], + "status_global_attr.{$linkField} = " . self::PRODUCT_TABLE_ALIAS . ".{$linkField}" + . ' AND status_global_attr.attribute_id = ' . (int)$statusAttribute->getAttributeId() + . ' AND status_global_attr.store_id = ' . Store::DEFAULT_STORE_ID, + [] + ); + + $select->joinLeft( + ['status_attr' => $statusAttribute->getBackendTable()], + "status_attr.{$linkField} = " . self::PRODUCT_TABLE_ALIAS . ".{$linkField}" + . ' AND status_attr.attribute_id = ' . (int)$statusAttribute->getAttributeId() + . ' AND status_attr.store_id = ' . $this->storeResolver->getCurrentStoreId(), + [] + ); + + $select->where('IFNULL(status_attr.value, status_global_attr.value) = ?', Status::STATUS_ENABLED); + + return $select; + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Website.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Website.php index 69d58ddefb700..b355c9e6374bb 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Website.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Website.php @@ -1,6 +1,6 @@ metadataPool = $metadataPool; + $this->resource = $resource; + $this->storeManager = $storeManager; + } + + /** + * Joins website-product relation table to filter products that are only in current website + * + * {@inheritdoc} + */ + public function process(Select $select) + { + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + $select->joinInner( + ['pw' => $this->resource->getTableName('catalog_product_website')], + 'pw.product_id = ' . BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS . '.' . $linkField + . ' AND pw.website_id = ' . $this->storeManager->getWebsite()->getId(), + [] + ); + + return $select; + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Setup/PropertyMapper.php b/app/code/Magento/Catalog/Model/ResourceModel/Setup/PropertyMapper.php index 01f0238a03bcc..9afc57845e1b0 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Setup/PropertyMapper.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Setup/PropertyMapper.php @@ -2,7 +2,7 @@ /** * Catalog attribute property mapper * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model\ResourceModel\Setup; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Url.php b/app/code/Magento/Catalog/Model/ResourceModel/Url.php index cc7daa73ea2a6..89443b0fbe8c2 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Url.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Url.php @@ -1,6 +1,6 @@ resource = $appResource; } + /** + * Get instance of ScopePool + * + * @return \Magento\Framework\App\Config + * @deprecated + */ + private function getAppConfig() + { + if ($this->appConfig === null) { + $this->appConfig = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Config::class + ); + } + return $this->appConfig; + } + /** * Check url rewrite suffix - whether we can support it * @@ -103,6 +124,24 @@ public function afterSave() return parent::afterSave(); } + /** + * {@inheritdoc} + */ + public function afterDeleteCommit() + { + if ($this->isValueChanged()) { + $this->updateSuffixForUrlRewrites(); + if ($this->isCategorySuffixChanged()) { + $this->cacheTypeList->invalidate([ + \Magento\Framework\App\Cache\Type\Block::TYPE_IDENTIFIER, + \Magento\Framework\App\Cache\Type\Collection::TYPE_IDENTIFIER + ]); + } + } + + return parent::afterDeleteCommit(); + } + /** * Check is category suffix changed * @@ -135,7 +174,12 @@ protected function updateSuffixForUrlRewrites() } $entities = $this->urlFinder->findAllByData($dataFilter); $oldSuffixPattern = '~' . preg_quote($this->getOldValue()) . '$~'; - $suffix = $this->getValue(); + if ($this->getValue() !== null) { + $suffix = $this->getValue(); + } else { + $this->getAppConfig()->clean(); + $suffix = $this->_config->getValue($this->getPath()); + } foreach ($entities as $urlRewrite) { $bind = $urlRewrite->getIsAutogenerated() ? [UrlRewrite::REQUEST_PATH => preg_replace($oldSuffixPattern, $suffix, $urlRewrite->getRequestPath())] diff --git a/app/code/Magento/Catalog/Model/System/Config/Source/Inputtype.php b/app/code/Magento/Catalog/Model/System/Config/Source/Inputtype.php index 6202ffc1a4b51..d4cc7488d9235 100644 --- a/app/code/Magento/Catalog/Model/System/Config/Source/Inputtype.php +++ b/app/code/Magento/Catalog/Model/System/Config/Source/Inputtype.php @@ -1,6 +1,6 @@ mediaConfig = $mediaConfig; + $this->context = $context; + $this->filePath = $filePath; + $this->miscParams = $miscParams; + $this->encryptor = $encryptor; + } + + /** + * {@inheritdoc} + */ + public function getUrl() + { + return $this->context->getBaseUrl() . $this->getRelativePath(DIRECTORY_SEPARATOR); + } + + /** + * {@inheritdoc} + */ + public function getContentType() + { + return $this->contentType; + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->getAbsolutePath($this->context->getPath()); + } + + /** + * Subroutine for building path + * + * @param string $path + * @param string $item + * @return string + */ + private function join($path, $item) + { + return trim( + $path . ($item ? DIRECTORY_SEPARATOR . ltrim($item, DIRECTORY_SEPARATOR) : ''), + DIRECTORY_SEPARATOR + ); + } + + /** + * {@inheritdoc} + */ + public function getSourceFile() + { + return $this->mediaConfig->getBaseMediaPath() + . DIRECTORY_SEPARATOR . ltrim($this->filePath, DIRECTORY_SEPARATOR); + } + + /** + * Get source content type + * + * @return string + */ + public function getSourceContentType() + { + return $this->contentType; + } + + /** + * {@inheritdoc} + */ + public function getContent() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getFilePath() + { + return $this->filePath; + } + + /** + * {@inheritdoc} + * @return ContextInterface + */ + public function getContext() + { + return $this->context; + } + + /** + * {@inheritdoc} + */ + public function getModule() + { + return 'cache'; + } + + /** + * Retrieve part of path based on misc params + * + * @return string + */ + private function getMiscPath() + { + return $this->encryptor->hash(implode('_', $this->miscParams), Encryptor::HASH_VERSION_MD5); + } + + /** + * Generate absolute path + * + * @param string $result + * @return string + */ + private function getAbsolutePath($result) + { + $prefix = (substr($result, 0, 1) == DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : ''; + $result = $this->join($result, $this->getModule()); + $result = $this->join($result, $this->getMiscPath()); + $result = $this->join($result, $this->getFilePath()); + return $prefix . $result; + } + + /** + * Generate relative path + * + * @param string $result + * @return string + */ + private function getRelativePath($result) + { + $result = $this->join($result, $this->getModule()); + $result = $this->join($result, $this->getMiscPath()); + $result = $this->join($result, $this->getFilePath()); + return DIRECTORY_SEPARATOR . $result; + } +} diff --git a/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php b/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php new file mode 100644 index 0000000000000..f72447c6ca2c2 --- /dev/null +++ b/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php @@ -0,0 +1,59 @@ +mediaConfig = $mediaConfig; + $this->filesystem = $filesystem; + $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->mediaDirectory->create($this->mediaConfig->getBaseMediaPath()); + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->mediaDirectory->getAbsolutePath($this->mediaConfig->getBaseMediaPath()); + } + + /** + * {@inheritdoc} + */ + public function getBaseUrl() + { + return $this->mediaConfig->getBaseMediaUrl(); + } +} diff --git a/app/code/Magento/Catalog/Model/View/Asset/Placeholder.php b/app/code/Magento/Catalog/Model/View/Asset/Placeholder.php new file mode 100644 index 0000000000000..7fa55b27a4b99 --- /dev/null +++ b/app/code/Magento/Catalog/Model/View/Asset/Placeholder.php @@ -0,0 +1,181 @@ +context = $context; + $this->scopeConfig = $scopeConfig; + $this->assetRepo = $assetRepo; + $this->type = $type; + } + + /** + * {@inheritdoc} + */ + public function getUrl() + { + if ($this->getFilePath() !== null) { + $result = $this->context->getBaseUrl() . '/' . $this->getModule() . '/' . $this->getFilePath(); + } else { + $result = $this->assetRepo->getUrl("Magento_Catalog::images/product/placeholder/{$this->type}.jpg"); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function getContentType() + { + return $this->contentType; + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + if ($this->getFilePath() !== null) { + $result = $this->getContext()->getPath() + . DIRECTORY_SEPARATOR . $this->getModule() + . DIRECTORY_SEPARATOR . $this->getFilePath(); + } else { + $defaultPlaceholder = $this->assetRepo->createAsset( + "Magento_Catalog::images/product/placeholder/{$this->type}.jpg" + ); + try { + $result = $defaultPlaceholder->getSourceFile(); + } catch (NotFoundException $e) { + $result = null; + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function getSourceFile() + { + return $this->getPath(); + } + + /** + * Get source content type + * + * @return string + */ + public function getSourceContentType() + { + return $this->contentType; + } + + /** + * {@inheritdoc} + */ + public function getContent() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getFilePath() + { + if ($this->filePath !== null) { + return $this->filePath; + } + // check if placeholder defined in config + $isConfigPlaceholder = $this->scopeConfig->getValue( + "catalog/placeholder/{$this->type}_placeholder", + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $this->filePath = $isConfigPlaceholder; + + return $this->filePath; + } + + /** + * {@inheritdoc} + * @return ContextInterface + */ + public function getContext() + { + return $this->context; + } + + /** + * {@inheritdoc} + */ + public function getModule() + { + return 'placeholder'; + } +} diff --git a/app/code/Magento/Catalog/Model/Webapi/Product/Option/Type/Date.php b/app/code/Magento/Catalog/Model/Webapi/Product/Option/Type/Date.php index 779c725bd909e..aeeaf049556d4 100644 --- a/app/code/Magento/Catalog/Model/Webapi/Product/Option/Type/Date.php +++ b/app/code/Magento/Catalog/Model/Webapi/Product/Option/Type/Date.php @@ -1,6 +1,6 @@ localeDate = $localeDate; + } + + /** + * Set the current date to Special Price From attribute if it empty + * + * @param \Magento\Framework\Event\Observer $observer + * @return $this + */ + public function execute(\Magento\Framework\Event\Observer $observer) + { + /** @var $product \Magento\Catalog\Model\Product */ + $product = $observer->getEvent()->getProduct(); + if ($product->getSpecialPrice() && !$product->getSpecialFromDate()) { + $product->setData('special_from_date', $this->localeDate->date()); + } + + return $this; + } +} diff --git a/app/code/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChange.php b/app/code/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChange.php new file mode 100644 index 0000000000000..562c76830d4e6 --- /dev/null +++ b/app/code/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChange.php @@ -0,0 +1,78 @@ +config = $config; + $this->productAttributeRepository = $productAttributeRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + } + + /** + * Change scope for all price attributes according to + * 'Catalog Price Scope' configuration parameter value + * + * @param EventObserver $observer + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute(EventObserver $observer) + { + $this->searchCriteriaBuilder->addFilter('frontend_input', 'price'); + $criteria = $this->searchCriteriaBuilder->create(); + + $scope = $this->config->getValue(Store::XML_PATH_PRICE_SCOPE); + $scope = ($scope == Store::PRICE_SCOPE_WEBSITE) + ? ProductAttributeInterface::SCOPE_WEBSITE_TEXT + : ProductAttributeInterface::SCOPE_GLOBAL_TEXT; + + $priceAttributes = $this->productAttributeRepository->getList($criteria)->getItems(); + + /** @var ProductAttributeInterface $priceAttribute */ + foreach ($priceAttributes as $priceAttribute) { + $priceAttribute->setScope($scope); + $this->productAttributeRepository->save($priceAttribute); + } + } +} diff --git a/app/code/Magento/Catalog/Plugin/Block/Topmenu.php b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php index 72bf4468080e3..04b2dadf3b6f4 100644 --- a/app/code/Magento/Catalog/Plugin/Block/Topmenu.php +++ b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php @@ -1,6 +1,6 @@ cache = $cache; $this->isCacheEnabled = $cacheState->isEnabled(\Magento\Eav\Model\Cache\Type::TYPE_IDENTIFIER); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); } /** @@ -43,12 +54,12 @@ public function aroundGetAttributesUsedInListing( ) { $cacheId = self::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID . $config->getEntityTypeId() . '_' . $config->getStoreId(); if ($this->isCacheEnabled && ($attributes = $this->cache->load($cacheId))) { - return unserialize($attributes); + return $this->serializer->unserialize($attributes); } $attributes = $proceed(); if ($this->isCacheEnabled) { $this->cache->save( - serialize($attributes), + $this->serializer->serialize($attributes), $cacheId, [ \Magento\Eav\Model\Cache\Type::CACHE_TAG, @@ -71,12 +82,12 @@ public function aroundGetAttributesUsedForSortBy( $cacheId = self::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID . $config->getEntityTypeId() . '_' . $config->getStoreId(); if ($this->isCacheEnabled && ($attributes = $this->cache->load($cacheId))) { - return unserialize($attributes); + return $this->serializer->unserialize($attributes); } $attributes = $proceed(); if ($this->isCacheEnabled) { $this->cache->save( - serialize($attributes), + $this->serializer->serialize($attributes), $cacheId, [ \Magento\Eav\Model\Cache\Type::CACHE_TAG, diff --git a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php index b32d3f14517c8..82bee56cd9ef8 100644 --- a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php @@ -1,6 +1,6 @@ calculator = $calculator; + } + + /** + * Get raw value of "as low as" as a minimal among tier prices + * {@inheritdoc} + */ + public function getValue(SaleableInterface $saleableItem) + { + /** @var TierPrice $price */ + $price = $saleableItem->getPriceInfo()->getPrice(TierPrice::PRICE_CODE); + $tierPriceList = $price->getTierPriceList(); + + $tierPrices = []; + foreach ($tierPriceList as $tierPrice) { + /** @var AmountInterface $price */ + $price = $tierPrice['price']; + $tierPrices[] = $price->getValue(); + } + + return $tierPrices ? min($tierPrices) : null; + } + + /** + * Return calculated amount object that keeps "as low as" value + * {@inheritdoc} + */ + public function getAmount(SaleableInterface $saleableItem) + { + $value = $this->getValue($saleableItem); + + return $value === null + ? null + : $this->calculator->getAmount($value, $saleableItem); + } +} diff --git a/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php b/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php index bac187bb820fe..4187a9a208b09 100644 --- a/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php @@ -1,6 +1,6 @@ salableResolver = $salableResolver ?: ObjectManager::getInstance()->get(SalableResolverInterface::class); + $this->minimalPriceCalculator = $minimalPriceCalculator + ?: ObjectManager::getInstance()->get(MinimalPriceCalculatorInterface::class); + } + /** * @return string */ protected function _toHtml() { - if (!$this->getSaleableItem() || $this->getSaleableItem()->getCanShowPrice() === false) { + if (!$this->salableResolver->isSalable($this->getSaleableItem())) { return ''; } $result = parent::_toHtml(); - - try { - /** @var MsrpPrice $msrpPriceType */ - $msrpPriceType = $this->getSaleableItem()->getPriceInfo()->getPrice('msrp_price'); - } catch (\InvalidArgumentException $e) { - $this->_logger->critical($e); - return $this->wrapResult($result); - } - //Renders MSRP in case it is enabled - $product = $this->getSaleableItem(); - if ($msrpPriceType->canApplyMsrp($product) && $msrpPriceType->isMinimalPriceLessMsrp($product)) { + if ($this->isMsrpPriceApplicable()) { /** @var BasePriceBox $msrpBlock */ $msrpBlock = $this->rendererPool->createPriceRender( MsrpPrice::PRICE_CODE, @@ -56,6 +86,25 @@ protected function _toHtml() return $this->wrapResult($result); } + /** + * Check is MSRP applicable for the current product. + * + * @return bool + */ + protected function isMsrpPriceApplicable() + { + try { + /** @var MsrpPrice $msrpPriceType */ + $msrpPriceType = $this->getSaleableItem()->getPriceInfo()->getPrice('msrp_price'); + } catch (\InvalidArgumentException $e) { + $this->_logger->critical($e); + return false; + } + + $product = $this->getSaleableItem(); + return $msrpPriceType->canApplyMsrp($product) && $msrpPriceType->isMinimalPriceLessMsrp($product); + } + /** * Wrap with standard required container * @@ -77,11 +126,15 @@ protected function wrapResult($html) */ public function renderAmountMinimal() { - /** @var \Magento\Catalog\Pricing\Price\FinalPrice $price */ - $price = $this->getPriceType(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE); $id = $this->getPriceId() ? $this->getPriceId() : 'product-minimal-price-' . $this->getSaleableItem()->getId(); + + $amount = $this->minimalPriceCalculator->getAmount($this->getSaleableItem()); + if ($amount === null) { + return ''; + } + return $this->renderAmount( - $price->getMinimalPrice(), + $amount, [ 'display_label' => __('As low as'), 'price_id' => $id, @@ -110,13 +163,15 @@ public function hasSpecialPrice() */ public function showMinimalPrice() { + $minTierPrice = $this->minimalPriceCalculator->getValue($this->getSaleableItem()); + /** @var Price\FinalPrice $finalPrice */ $finalPrice = $this->getPriceType(Price\FinalPrice::PRICE_CODE); $finalPriceValue = $finalPrice->getAmount()->getValue(); - $minimalPriceAValue = $finalPrice->getMinimalPrice()->getValue(); + return $this->getDisplayMinimalPrice() - && $minimalPriceAValue - && $minimalPriceAValue < $finalPriceValue; + && $minTierPrice !== null + && $minTierPrice < $finalPriceValue; } /** diff --git a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php index 92ff22decb1d0..156fbb8633699 100644 --- a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php +++ b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php @@ -1,6 +1,6 @@ getConnection() ->createTable($table); + $customerGroupTable = $setup->getConnection()->describeTable($setup->getTable('customer_group')); + $customerGroupIdType = $customerGroupTable['customer_group_id']['DATA_TYPE'] == 'int' + ? \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER : $customerGroupTable['customer_group_id']['DATA_TYPE']; /** * Create table 'catalog_product_entity_tier_price' */ @@ -1883,7 +1887,7 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con ) ->addColumn( 'customer_group_id', - \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT, + $customerGroupIdType, null, ['unsigned' => true, 'nullable' => false, 'default' => '0'], 'Customer Group ID' @@ -2426,7 +2430,6 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con 'option_id', $installer->getTable('catalog_product_option'), 'option_id', - \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE, \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE ) ->setComment( @@ -2937,7 +2940,7 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con ) ->addColumn( 'customer_group_id', - \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT, + $customerGroupIdType, null, ['unsigned' => true, 'nullable' => false, 'primary' => true], 'Customer Group ID' @@ -3056,7 +3059,7 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con ) ->addColumn( 'customer_group_id', - \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT, + $customerGroupIdType, null, ['unsigned' => true, 'nullable' => false, 'primary' => true], 'Customer Group ID' diff --git a/app/code/Magento/Catalog/Setup/Recurring.php b/app/code/Magento/Catalog/Setup/Recurring.php index 85484cdddb218..e6cb302747918 100644 --- a/app/code/Magento/Catalog/Setup/Recurring.php +++ b/app/code/Magento/Catalog/Setup/Recurring.php @@ -1,6 +1,6 @@ categorySetupFactory = $categorySetupFactory; $this->eavSetupFactory = $eavSetupFactory; + $this->attributeCache = $attributeCache; } /** @@ -135,19 +146,24 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface } if (version_compare($context->getVersion(), '2.0.4') < 0) { + $mediaBackendType = 'static'; + $mediaBackendModel = null; /** @var \Magento\Catalog\Setup\CategorySetup $categorySetup */ $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]); $categorySetup->updateAttribute( 'catalog_product', 'media_gallery', 'backend_type', - 'static' + $mediaBackendType ); $categorySetup->updateAttribute( 'catalog_product', 'media_gallery', - 'backend_model' + 'backend_model', + $mediaBackendModel ); + + $this->changeMediaGalleryAttributeInCache($mediaBackendType, $mediaBackendModel); } if (version_compare($context->getVersion(), '2.0.5', '<')) { @@ -340,10 +356,10 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); } - + if (version_compare($context->getVersion(), '2.0.7') < 0) { - /** @var EavSetup $eavSetupF */ - $eavSetup= $this->eavSetupFactory->create(['setup' => $setup]); + /** @var EavSetup $eavSetup */ + $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); $eavSetup->updateAttribute( ProductAttributeInterface::ENTITY_TYPE_CODE, @@ -353,7 +369,53 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); } - + + if (version_compare($context->getVersion(), '2.1.3') < 0) { + /** @var \Magento\Catalog\Setup\CategorySetup $categorySetup */ + $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]); + $this->changePriceAttributeDefaultScope($categorySetup); + } + $setup->endSetup(); } + + /** + * @param \Magento\Catalog\Setup\CategorySetup $categorySetup + * @return void + */ + private function changePriceAttributeDefaultScope($categorySetup) + { + $entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY); + foreach (['price', 'cost', 'special_price'] as $attributeCode) { + $attribute = $categorySetup->getAttribute($entityTypeId, $attributeCode); + $categorySetup->updateAttribute( + $entityTypeId, + $attribute['attribute_id'], + 'is_global', + \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL + ); + + } + } + + /** + * @param string $mediaBackendType + * @param string $mediaBackendModel + * @return void + */ + private function changeMediaGalleryAttributeInCache($mediaBackendType, $mediaBackendModel) + { + // need to do, because media_gallery has backend model in cache. + $catalogProductAttributes = $this->attributeCache->getAttributes('catalog_product', '0-0'); + + if (is_array($catalogProductAttributes)) { + /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $catalogProductAttribute */ + foreach ($catalogProductAttributes as $catalogProductAttribute) { + if ($catalogProductAttribute->getAttributeCode() == 'media_gallery') { + $catalogProductAttribute->setBackendModel($mediaBackendModel); + $catalogProductAttribute->setBackendType($mediaBackendType); + } + } + } + } } diff --git a/app/code/Magento/Catalog/Setup/UpgradeSchema.php b/app/code/Magento/Catalog/Setup/UpgradeSchema.php old mode 100644 new mode 100755 index 9683632f121b6..db0aaa3a010e3 --- a/app/code/Magento/Catalog/Setup/UpgradeSchema.php +++ b/app/code/Magento/Catalog/Setup/UpgradeSchema.php @@ -1,6 +1,6 @@ addUniqueKeyToCategoryProductTable($setup); } - if (version_compare($context->getVersion(), '2.1.0', '<')) { + if (version_compare($context->getVersion(), '2.1.4', '<')) { $this->addPercentageValueColumn($setup); + $tables = [ + 'catalog_product_index_price_cfg_opt_agr_idx', + 'catalog_product_index_price_cfg_opt_agr_tmp', + 'catalog_product_index_price_cfg_opt_idx', + 'catalog_product_index_price_cfg_opt_tmp', + 'catalog_product_index_price_final_idx', + 'catalog_product_index_price_final_tmp', + 'catalog_product_index_price_idx', + 'catalog_product_index_price_opt_agr_idx', + 'catalog_product_index_price_opt_agr_tmp', + 'catalog_product_index_price_opt_idx', + 'catalog_product_index_price_opt_tmp', + 'catalog_product_index_price_tmp', + ]; + foreach ($tables as $table) { + $setup->getConnection()->modifyColumn( + $setup->getTable($table), + 'customer_group_id', + ['type' => 'integer', 'nullable' => false] + ); + } + $this->addSourceEntityIdToProductEavIndex($setup); + $this->recreateCatalogCategoryProductIndexTmpTable($setup); } + $setup->endSetup(); } + /** + * Add the column 'source_id' to the Product EAV index tables. + * It allows to identify which entity was used to create value in the index. + * It is useful to identify original entity in a composite products. + * + * @param SchemaSetupInterface $setup + * @return void + */ + private function addSourceEntityIdToProductEavIndex(SchemaSetupInterface $setup) + { + $tables = [ + 'catalog_product_index_eav', + 'catalog_product_index_eav_idx', + 'catalog_product_index_eav_tmp', + 'catalog_product_index_eav_decimal', + 'catalog_product_index_eav_decimal_idx', + 'catalog_product_index_eav_decimal_tmp', + ]; + $connection = $setup->getConnection(); + foreach ($tables as $tableName) { + $tableName = $setup->getTable($tableName); + if (!$connection->tableColumnExists($tableName, 'source_id')) { + $connection->addColumn( + $tableName, + 'source_id', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + 'unsigned' => true, + 'nullable' => false, + 'default' => 0, + 'comment' => 'Original entity Id for attribute value', + ] + ); + $connection->dropIndex($tableName, $connection->getPrimaryKeyName($tableName)); + $primaryKeyFields = ['entity_id', 'attribute_id', 'store_id', 'value', 'source_id']; + $setup->getConnection()->addIndex( + $tableName, + $connection->getIndexName($tableName, $primaryKeyFields), + $primaryKeyFields, + \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_PRIMARY + ); + } + } + } + /** * @param SchemaSetupInterface $setup * @return void @@ -291,16 +360,90 @@ private function removeGroupPrice(SchemaSetupInterface $setup) private function addPercentageValueColumn(SchemaSetupInterface $setup) { $connection = $setup->getConnection(); - $connection->addColumn( - $setup->getTable('catalog_product_entity_tier_price'), - 'percentage_value', - [ - 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, - 'nullable' => true, - 'length' => '5,2', - 'comment' => 'Percentage value', - 'after' => 'value' - ] - ); + $tableName = $setup->getTable('catalog_product_entity_tier_price'); + + if (!$connection->tableColumnExists($tableName, 'percentage_value')) { + $connection->addColumn( + $tableName, + 'percentage_value', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + 'nullable' => true, + 'length' => '5,2', + 'comment' => 'Percentage value', + 'after' => 'value' + ] + ); + } + } + + /** + * Drop and recreate catalog_category_product_index_tmp table + * + * Before this update the catalog_category_product_index_tmp table was created without usage of PK + * and with engine=MEMORY. Such structure of catalog_category_product_index_tmp table causes + * issues with MySQL DB replication. + * + * To avoid replication issues this method drops catalog_category_product_index_tmp table + * and creates new one with PK and engine=InnoDB + * + * @param SchemaSetupInterface $setup + * @return void + */ + private function recreateCatalogCategoryProductIndexTmpTable(SchemaSetupInterface $setup) + { + $tableName = $setup->getTable('catalog_category_product_index_tmp'); + + // Drop catalog_category_product_index_tmp table + $setup->getConnection()->dropTable($tableName); + + // Create catalog_category_product_index_tmp table with PK and engine=InnoDB + $table = $setup->getConnection() + ->newTable($tableName) + ->addColumn( + 'category_id', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['unsigned' => true, 'nullable' => false, 'primary' => true, 'default' => '0'], + 'Category ID' + ) + ->addColumn( + 'product_id', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['unsigned' => true, 'nullable' => false, 'primary' => true, 'default' => '0'], + 'Product ID' + ) + ->addColumn( + 'position', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['nullable' => false, 'default' => '0'], + 'Position' + ) + ->addColumn( + 'is_parent', + \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT, + null, + ['unsigned' => true, 'nullable' => false, 'default' => '0'], + 'Is Parent' + ) + ->addColumn( + 'store_id', + \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT, + null, + ['unsigned' => true, 'nullable' => false, 'primary' => true, 'default' => '0'], + 'Store ID' + ) + ->addColumn( + 'visibility', + \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT, + null, + ['unsigned' => true, 'nullable' => false], + 'Visibility' + ) + ->setComment('Catalog Category Product Indexer temporary table'); + + $setup->getConnection()->createTable($table); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Category/AbstractCategoryTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Category/AbstractCategoryTest.php index cb7d1c20b3356..f22f7f50859a2 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Category/AbstractCategoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Category/AbstractCategoryTest.php @@ -1,6 +1,6 @@ context = $this->getMockBuilder(\Magento\Backend\Block\Context::class) @@ -53,13 +56,7 @@ public function testToHtml() ->disableOriginalConstructor() ->setMethods(['dispatch']) ->getMock(); - $eventManager->expects($this->once())->method('dispatch')->will($this->returnValue(true)); - - $scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config::class) - ->setMethods(['getValue']) - ->disableOriginalConstructor()->getMock(); - $scopeConfig->expects($this->once())->method('getValue')->withAnyParameters() - ->will($this->returnValue(false)); + $eventManager->expects($this->exactly(2))->method('dispatch')->will($this->returnValue(true)); $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)->disableOriginalConstructor() ->setMethods(['setStoreId', 'load', 'getId', '__wakeup', '__sleep']) @@ -93,8 +90,6 @@ public function testToHtml() $this->context->expects($this->once())->method('getEventManager') ->will($this->returnValue($eventManager)); - $this->context->expects($this->once())->method('getScopeConfig') - ->will($this->returnValue($scopeConfig)); $this->context->expects($this->once())->method('getLayout') ->will($this->returnValue($layout)); $this->context->expects($this->once())->method('getRequest') diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Rss/Grid/LinkTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Rss/Grid/LinkTest.php index 8bc6675933768..11efb045a1c2f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Rss/Grid/LinkTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Rss/Grid/LinkTest.php @@ -1,6 +1,6 @@ productMock->expects($this->once()) ->method('getIdentities') @@ -154,9 +154,9 @@ public function testGetAddToCartPostParams() ]; $this->typeInstanceMock->expects($this->once()) - ->method('hasRequiredOptions') + ->method('isPossibleBuyFromList') ->with($this->equalTo($this->productMock)) - ->will($this->returnValue(false)); + ->will($this->returnValue(true)); $this->cartHelperMock->expects($this->any()) ->method('getAddUrl') ->with($this->equalTo($this->productMock), $this->equalTo([])) diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ListTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ListTest.php index 786185e60e9db..de0e3d5f0d5ef 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ListTest.php @@ -1,6 +1,6 @@ method('init') ->willReturnMap([ [$productMock, 'product_page_image_small', [], $this->imageHelper], - [$productMock, 'product_page_image_medium', [], $this->imageHelper], - [$productMock, 'product_page_image_large', [], $this->imageHelper], + [$productMock, 'product_page_image_medium_no_frame', [], $this->imageHelper], + [$productMock, 'product_page_image_large_no_frame', [], $this->imageHelper], ]) ->willReturnSelf(); $this->imageHelper->expects($this->exactly(3)) @@ -129,19 +129,6 @@ public function testGetGalleryImages() ->method('getUrl') ->willReturn('product_page_image_large_url'); - $this->imageHelper->expects($this->exactly(2)) - ->method('constrainOnly') - ->with(true) - ->willReturnSelf(); - $this->imageHelper->expects($this->exactly(2)) - ->method('keepAspectRatio') - ->with(true) - ->willReturnSelf(); - $this->imageHelper->expects($this->exactly(2)) - ->method('keepFrame') - ->with(false) - ->willReturnSelf(); - $images = $this->model->getGalleryImages(); $this->assertInstanceOf(\Magento\Framework\Data\Collection::class, $images); } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/OptionsTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/OptionsTest.php index 0e8d55232221a..9bafb60fc5b44 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/OptionsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/OptionsTest.php @@ -1,6 +1,6 @@ productTypeConfig = $this->getMock(\Magento\Catalog\Model\ProductTypes\ConfigInterface::class); $this->registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false); - $this->view = $helper->getObject( + $this->view = $helper->getObject( \Magento\Catalog\Block\Product\View::class, ['productTypeConfig' => $this->productTypeConfig, 'registry' => $this->registryMock] ); @@ -65,7 +65,7 @@ public function testShouldRenderQuantity() public function testGetIdentities() { - $productTags = ['catalog_product_1']; + $productTags = ['cat_p_1']; $product = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); $category = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); @@ -84,6 +84,6 @@ public function testGetIdentities() ] ) ); - $this->assertEquals(['catalog_product_1', 'catalog_category_1'], $this->view->getIdentities()); + $this->assertEquals(['cat_p_1', 'cat_c_1'], $this->view->getIdentities()); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/Widget/NewWidgetTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/Widget/NewWidgetTest.php index cfd009787158a..251eda5ce32fa 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/Widget/NewWidgetTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/Widget/NewWidgetTest.php @@ -1,6 +1,6 @@ objectManager = new ObjectManagerHelper($this); @@ -63,7 +66,7 @@ protected function setUp() false, false ); - $this->scopeConfig = $this->getMock(\Magento\Framework\App\Config::class, ['getValue'], [], '', false, false); + $this->scopeConfig = $this->getMock(\Magento\Framework\App\Config::class, [], [], '', false, false); $this->cacheState = $this->getMock( \Magento\Framework\App\Cache\State::class, ['isEnabled'], @@ -109,6 +112,9 @@ protected function setUp() ); } + /** + * @inheritdoc + */ protected function tearDown() { $this->block = null; @@ -168,6 +174,9 @@ public function testGetCurrentPage($pageNumber, $expectedResult) $this->assertEquals($expectedResult, $this->block->getCurrentPage()); } + /** + * @return array + */ public function getCurrentPageDataProvider() { return [ @@ -184,12 +193,13 @@ public function testGetProductsCount() $this->assertEquals(2, $this->block->getProductsCount()); } + /** + * @return void + */ protected function generalGetProductCollection() { - $this->eventManager->expects($this->once())->method('dispatch') + $this->eventManager->expects($this->exactly(2))->method('dispatch') ->will($this->returnValue(true)); - $this->scopeConfig->expects($this->once())->method('getValue')->withAnyParameters() - ->willReturn(false); $this->cacheState->expects($this->atLeastOnce())->method('isEnabled')->withAnyParameters() ->willReturn(false); $this->catalogConfig->expects($this->once())->method('getProductAttributes') @@ -329,6 +339,9 @@ public function testGetProductAllCollection($pagerEnable, $productsCount, $produ ); } + /** + * @return array + */ public function getProductCollectionDataProvider() { return [ diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Rss/CategoryTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Rss/CategoryTest.php index e7e8171868f94..7a58617b90d58 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Rss/CategoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Rss/CategoryTest.php @@ -1,6 +1,6 @@ objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + } + + public function executeDataProvider() + { + return [ + ['image1', 'image1'], + ['image2', 'image2'], + [null, 'image'], + ]; + } + + /** + * @param string $name + * @param string $savedName + * + * @dataProvider executeDataProvider + */ + public function testExecute($name, $savedName) + { + $request = $this->objectManager->getObject(Request::class); + + $uploader = $this->getMock(ImageUploader::class, ['saveFileToTmpDir'], [], '', false); + + $resultFactory = $this->getMock(ResultFactory::class, ['create'], [], '', false); + + $resultFactory->expects($this->once()) + ->method('create') + ->will($this->returnValue(new DataObject())); + + $model = $this->objectManager->getObject(Model::class, [ + 'request' => $request, + 'resultFactory' => $resultFactory, + 'imageUploader' => $uploader + ]); + + $uploader->expects($this->once()) + ->method('saveFileToTmpDir') + ->with($savedName) + ->will($this->returnValue([])); + + $request->setParam('param_name', $name); + + $model->execute(); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/SaveTest.php index 51d99f7219575..6c6fa4c3a10ab 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/SaveTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/SaveTest.php @@ -1,10 +1,12 @@ markTestSkipped('Due to MAGETWO-48956'); $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->contextMock = $this->getMock( - \Magento\Backend\App\Action\Context::class, - [ - 'getTitle', - 'getRequest', - 'getObjectManager', - 'getEventManager', - 'getResponse', - 'getMessageManager', - 'getResultRedirectFactory' - ], - [], - '', - false - ); $this->resultRedirectFactoryMock = $this->getMock( \Magento\Backend\Model\View\Result\RedirectFactory::class, ['create'], @@ -109,13 +74,6 @@ protected function setUp() '', false ); - $this->resultRawFactoryMock = $this->getMock( - \Magento\Framework\Controller\Result\RawFactory::class, - [], - [], - '', - false - ); $this->resultJsonFactoryMock = $this->getMock( \Magento\Framework\Controller\Result\JsonFactory::class, ['create'], @@ -151,12 +109,6 @@ protected function setUp() true, ['dispatch'] ); - $this->responseMock = $this->getMockForAbstractClass( - \Magento\Framework\App\ResponseInterface::class, - [], - '', - false - ); $this->messageManagerMock = $this->getMockForAbstractClass( \Magento\Framework\Message\ManagerInterface::class, [], @@ -167,23 +119,15 @@ protected function setUp() ['addSuccess', 'getMessages'] ); - $this->contextMock->expects($this->any())->method('getTitle')->willReturn($this->titleMock); - $this->contextMock->expects($this->any())->method('getRequest')->willReturn($this->requestMock); - $this->contextMock->expects($this->any())->method('getObjectManager')->willReturn($this->objectManagerMock); - $this->contextMock->expects($this->any())->method('getEventManager')->willReturn($this->eventManagerMock); - $this->contextMock->expects($this->any())->method('getResponse')->willReturn($this->responseMock); - $this->contextMock->expects($this->any())->method('getMessageManager')->willReturn($this->messageManagerMock); - $this->contextMock->expects($this->any()) - ->method('getResultRedirectFactory') - ->willReturn($this->resultRedirectFactoryMock); - $this->save = $this->objectManager->getObject( \Magento\Catalog\Controller\Adminhtml\Category\Save::class, [ - 'context' => $this->contextMock, - 'resultRawFactory' => $this->resultRawFactoryMock, + 'request' => $this->requestMock, + 'eventManager' => $this->eventManagerMock, + 'messageManager' => $this->messageManagerMock, 'resultJsonFactory' => $this->resultJsonFactoryMock, - 'layoutFactory' => $this->layoutFactoryMock + 'layoutFactory' => $this->layoutFactoryMock, + 'resultRedirectFactory' => $this->resultRedirectFactoryMock ] ); } @@ -201,6 +145,8 @@ protected function setUp() */ public function testExecute($categoryId, $storeId, $parentId) { + $this->markTestSkipped('Due to MAGETWO-48956'); + $rootCategoryId = \Magento\Catalog\Model\Category::TREE_ROOT_ID; $products = [['any_product']]; $postData = [ @@ -577,4 +523,95 @@ public function dataProviderExecute() ] ]; } + + /** + * @return array + */ + public function imagePreprocessingDataProvider() + { + return [ + [['attribute1' => null, 'attribute2' => 123]], + [['attribute2' => 123]] + ]; + } + + /** + * @dataProvider imagePreprocessingDataProvider + * + * @param array $data + */ + public function testImagePreprocessingWithoutValue($data) + { + $eavConfig = $this->getMock(\Magento\Eav\Model\Config::class, ['getEntityType'], [], '', false); + + $imageBackendModel = $this->objectManager->getObject( + \Magento\Catalog\Model\Category\Attribute\Backend\Image::class + ); + + $collection = new \Magento\Framework\DataObject(['attribute_collection' => [ + new \Magento\Framework\DataObject([ + 'attribute_code' => 'attribute1', + 'backend' => $imageBackendModel + ]), + new \Magento\Framework\DataObject([ + 'attribute_code' => 'attribute2', + 'backend' => new \Magento\Framework\DataObject() + ]) + ]]); + + $eavConfig->expects($this->once()) + ->method('getEntityType') + ->with(\Magento\Catalog\Api\Data\CategoryAttributeInterface::ENTITY_TYPE_CODE) + ->will($this->returnValue($collection)); + + $model = $this->objectManager->getObject(\Magento\Catalog\Controller\Adminhtml\Category\Save::class, [ + 'eavConfig' => $eavConfig + ]); + + $result = $model->imagePreprocessing($data); + + $this->assertEquals([ + 'attribute1' => false, + 'attribute2' => 123 + ], $result); + } + + public function testImagePreprocessingWithValue() + { + $eavConfig = $this->getMock(\Magento\Eav\Model\Config::class, ['getEntityType'], [], '', false); + + $imageBackendModel = $this->objectManager->getObject( + \Magento\Catalog\Model\Category\Attribute\Backend\Image::class + ); + + $collection = new \Magento\Framework\DataObject(['attribute_collection' => [ + new \Magento\Framework\DataObject([ + 'attribute_code' => 'attribute1', + 'backend' => $imageBackendModel + ]), + new \Magento\Framework\DataObject([ + 'attribute_code' => 'attribute2', + 'backend' => new \Magento\Framework\DataObject() + ]) + ]]); + + $eavConfig->expects($this->once()) + ->method('getEntityType') + ->with(\Magento\Catalog\Api\Data\CategoryAttributeInterface::ENTITY_TYPE_CODE) + ->will($this->returnValue($collection)); + + $model = $this->objectManager->getObject(Model::class, [ + 'eavConfig' => $eavConfig + ]); + + $result = $model->imagePreprocessing([ + 'attribute1' => 'somevalue', + 'attribute2' => null + ]); + + $this->assertEquals([ + 'attribute1' => 'somevalue', + 'attribute2' => null + ], $result); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/Widget/CategoriesJsonTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/Widget/CategoriesJsonTest.php index 824d1c7bf23f2..59b2238aacdbc 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/Widget/CategoriesJsonTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/Widget/CategoriesJsonTest.php @@ -1,7 +1,7 @@ objectManager = new ObjectManager($this); + $this->contextMock = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productBuilderMock = $this->getMockBuilder(ProductBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultJsonFactoryMock = $this->getMockBuilder(JsonFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->setMethods(['getParam', 'setParam']) + ->getMockForAbstractClass(); + $this->contextMock->expects($this->once()) + ->method('getRequest') + ->willReturn($this->requestMock); + $this->attributeSetRepositoryMock = $this->getMockBuilder(AttributeSetRepositoryInterface::class) + ->setMethods(['get']) + ->getMockForAbstractClass(); + $this->attributeSetInterfaceMock = $this->getMockBuilder(AttributeSetInterface::class) + ->getMockForAbstractClass(); + $this->searchCriteriaBuilderMock = $this->getMockBuilder(SearchCriteriaBuilder::class) + ->disableOriginalConstructor() + ->setMethods(['addFilter', 'create', 'setPageSize']) + ->getMockForAbstractClass(); + $this->searchCriteriaMock = $this->getMockBuilder(SearchCriteria::class) + ->disableOriginalConstructor() + ->getMock(); + $this->attributeGroupRepositoryMock = $this->getMockBuilder(AttributeGroupRepositoryInterface::class) + ->setMethods(['getList']) + ->getMockForAbstractClass(); + $this->attributeGroupSearchResultsMock = $this->getMockBuilder(AttributeGroupSearchResultsInterface::class) + ->setMethods(['getItems']) + ->getMockForAbstractClass(); + $this->attributeGroupInterfaceFactoryMock = $this->getMockBuilder(AttributeGroupInterfaceFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->attributeGroupInterfaceMock = $this->getMockBuilder(AttributeGroupInterface::class) + ->setMethods(['getExtensionAttributes']) + ->getMockForAbstractClass(); + $this->jsonMock = $this->getMockBuilder(Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->controller = $this->objectManager->getObject( + AddAttributeToTemplate::class, + [ + 'context' => $this->contextMock, + 'productBuilder' => $this->productBuilderMock, + 'resultJsonFactory' => $this->resultJsonFactoryMock, + ] + ); + + $this->objectManager->setBackwardCompatibleProperty( + $this->controller, + 'attributeSetRepository', + $this->attributeSetRepositoryMock + ); + $this->objectManager->setBackwardCompatibleProperty( + $this->controller, + 'searchCriteriaBuilder', + $this->searchCriteriaBuilderMock + ); + $this->objectManager->setBackwardCompatibleProperty( + $this->controller, + 'attributeGroupRepository', + $this->attributeGroupRepositoryMock + ); + $this->objectManager->setBackwardCompatibleProperty( + $this->controller, + 'attributeGroupFactory', + $this->attributeGroupInterfaceFactoryMock + ); + } + + public function testExecuteWithoutAttributeGroupItems() + { + $groupCode = 'attributes'; + $groupName = 'Attributes'; + $groupSortOrder = '15'; + $templateId = '4'; + $attributeIds = [ + 'selected' => ["178"], + 'total' => '1' + ]; + + $this->requestMock + ->expects($this->any()) + ->method('getParam') + ->willReturnMap( + [ + ['groupCode', null, $groupCode], + ['groupName', null, $groupName], + ['groupSortOrder', null, $groupSortOrder], + ['templateId', null, $templateId], + ['attributeIds', [], $attributeIds] + ] + ); + + $this->attributeSetRepositoryMock->expects($this->once()) + ->method('get') + ->willReturn($this->attributeSetInterfaceMock); + + $this->searchCriteriaBuilderMock->expects($this->any()) + ->method('addFilter') + ->willReturnSelf(); + $this->searchCriteriaBuilderMock->expects($this->any()) + ->method('create') + ->willReturn($this->searchCriteriaMock); + $this->searchCriteriaBuilderMock->expects($this->once()) + ->method('setPageSize') + ->willReturnSelf(); + $this->searchCriteriaBuilderMock->expects($this->never()) + ->method('addSortOrder') + ->willReturnSelf(); + + $this->attributeGroupRepositoryMock->expects($this->once()) + ->method('getList') + ->willReturn($this->attributeGroupSearchResultsMock); + $this->attributeGroupSearchResultsMock->expects($this->once()) + ->method('getItems') + ->willReturn(null); + + $this->attributeGroupInterfaceFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->attributeGroupInterfaceMock); + $this->attributeGroupInterfaceMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willThrowException(new LocalizedException(__('Could not get extension attributes'))); + + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->jsonMock); + $this->jsonMock->expects($this->once())->method('setJsonData') + ->willReturnSelf(); + + $this->controller->execute(); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/EditTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/EditTest.php index be6cd55729922..b2eb887f04b7a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/EditTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/EditTest.php @@ -1,6 +1,6 @@ objectManager = new ObjectManager($this); - $this->productLinkFactoryMock = $this->getMockBuilder(ProductLinkInterfaceFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->productRepositoryMock = $this->getMockBuilder(ProductRepository::class) - ->disableOriginalConstructor() - ->getMock(); $this->requestMock = $this->getMockBuilder(RequestInterface::class) ->setMethods(['getPost']) ->getMockForAbstractClass(); - $this->storeMock = $this->getMockBuilder(StoreInterface::class) - ->setMethods(['getWebsite']) - ->getMockForAbstractClass(); - $this->websiteMock = $this->getMockBuilder(WebsiteInterface::class) - ->getMockForAbstractClass(); $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) ->getMockForAbstractClass(); - $this->dateFilterMock = $this->getMockBuilder(DateFilter::class) - ->disableOriginalConstructor() - ->getMock(); $this->stockFilterMock = $this->getMockBuilder(StockDataFilter::class) ->disableOriginalConstructor() ->getMock(); $this->productMock = $this->getMockBuilder(Product::class) - ->setMethods([ - 'setData', - 'addData', - 'getId', - 'setWebsiteIds', - 'isLockedAttribute', - 'lockAttribute', - 'getAttributes', - 'unlockAttribute', - 'getOptionsReadOnly', - 'setOptions', - 'setCanSaveCustomOptions', - '__sleep', - '__wakeup', - 'getSku', - 'getProductLinks', - 'getWebsiteIds' - ]) + ->setMethods( + [ + 'getId', + 'isLockedAttribute', + 'lockAttribute', + 'getAttributes', + 'unlockAttribute', + 'getOptionsReadOnly', + 'getSku', + 'getProductLinks', + ] + ) ->disableOriginalConstructor() - ->getMock(); + ->getMockForAbstractClass(); $this->customOptionFactoryMock = $this->getMockBuilder(ProductCustomOptionInterfaceFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->customOptionMock = $this->getMockBuilder(ProductCustomOptionInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); $this->productLinksMock = $this->getMockBuilder(ProductLinks::class) ->disableOriginalConstructor() ->getMock(); - $this->productLinksMock->expects($this->any()) ->method('initializeLinks') ->willReturn($this->productMock); @@ -175,10 +112,7 @@ protected function setUp() 'storeManager' => $this->storeManagerMock, 'stockFilter' => $this->stockFilterMock, 'productLinks' => $this->productLinksMock, - 'dateFilter' => $this->dateFilterMock, 'customOptionFactory' => $this->customOptionFactoryMock, - 'productLinkFactory' => $this->productLinkFactoryMock, - 'productRepository' => $this->productRepositoryMock, ]); $this->linkResolverMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Link\Resolver::class) @@ -192,22 +126,23 @@ protected function setUp() /** * @covers \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper::initialize - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @param bool $isSingleStore + * @param array $websiteIds + * @param array $expWebsiteIds + * + * @dataProvider initializeDataProvider */ - public function testInitialize() + public function testInitialize($isSingleStore, $websiteIds, $expWebsiteIds) { - $this->customOptionMock->expects($this->once()) - ->method('setProductSku'); - $this->customOptionMock->expects($this->once()) - ->method('setOptionId'); - $optionsData = [ - 'option1' => ['is_delete' => true, 'name' => 'name1', 'price' => 'price1'], - 'option2' => ['is_delete' => false, 'name' => 'name1', 'price' => 'price1'], + 'option1' => ['is_delete' => true, 'name' => 'name1', 'price' => 'price1', 'option_id' => ''], + 'option2' => ['is_delete' => false, 'name' => 'name1', 'price' => 'price1', 'option_id' => '13'], + 'option3' => ['is_delete' => false, 'name' => 'name1', 'price' => 'price1', 'option_id' => '14'] ]; $productData = [ 'stock_data' => ['stock_data'], 'options' => $optionsData, + 'website_ids' => $websiteIds ]; $attributeNonDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) ->disableOriginalConstructor() @@ -224,82 +159,95 @@ public function testInitialize() ->disableOriginalConstructor() ->getMock(); - $attributeNonDate->expects($this->any()) - ->method('getBackend') - ->willReturn($attributeNonDateBackEnd); - $attributeDate->expects($this->any()) - ->method('getBackend') - ->willReturn($attributeDateBackEnd); - $this->productMock->expects($this->any()) - ->method('getProductLinks') - ->willReturn([]); - $attributeNonDateBackEnd->expects($this->any()) - ->method('getType') - ->willReturn('non-datetime'); - $attributeDateBackEnd->expects($this->any()) - ->method('getType') - ->willReturn('datetime'); - - $attributesArray = [ - $attributeNonDate, - $attributeDate - ]; + $attributeNonDate->expects($this->any())->method('getBackend')->willReturn($attributeNonDateBackEnd); + $attributeDate->expects($this->any())->method('getBackend')->willReturn($attributeDateBackEnd); + $this->productMock->expects($this->any())->method('getProductLinks')->willReturn([]); + $attributeNonDateBackEnd->expects($this->any())->method('getType')->willReturn('non-datetime'); + $attributeDateBackEnd->expects($this->any())->method('getType')->willReturn('datetime'); $useDefaults = ['attributeCode1', 'attributeCode2']; - $this->requestMock->expects($this->at(0)) - ->method('getPost') - ->with('product') - ->willReturn($productData); - $this->requestMock->expects($this->at(1)) - ->method('getPost') - ->with('use_default') - ->willReturn($useDefaults); + $this->requestMock->expects($this->any())->method('getPost')->willReturnMap( + [ + ['product', [], $productData], + ['use_default', null, $useDefaults] + ] + ); $this->linkResolverMock->expects($this->once())->method('getLinks')->willReturn([]); - $this->stockFilterMock->expects($this->once()) - ->method('filter') - ->with(['stock_data']) + $this->stockFilterMock->expects($this->once())->method('filter')->with(['stock_data']) ->willReturn(['stock_data']); - $this->productMock->expects($this->once()) - ->method('isLockedAttribute') - ->with('media') - ->willReturn(true); - $this->productMock->expects($this->once()) - ->method('unlockAttribute') - ->with('media'); - $this->productMock->expects($this->any()) - ->method('getProductLinks') - ->willReturn([]); - $this->productMock->expects($this->once()) - ->method('lockAttribute') - ->with('media'); - $this->productMock->expects($this->once()) - ->method('getAttributes') - ->willReturn($attributesArray); - - $productData['category_ids'] = []; - $productData['website_ids'] = []; - unset($productData['options']); - - $this->productMock->expects($this->once()) - ->method('addData') - ->with($productData); - $this->productMock->expects($this->once()) - ->method('getSku') - ->willReturn('sku'); - $this->productMock->expects($this->any()) - ->method('getOptionsReadOnly') - ->willReturn(false); - + $this->productMock->expects($this->once())->method('isLockedAttribute')->with('media')->willReturn(true); + $this->productMock->expects($this->once())->method('unlockAttribute')->with('media'); + $this->productMock->expects($this->any())->method('getProductLinks')->willReturn([]); + $this->productMock->expects($this->once())->method('lockAttribute')->with('media'); + $this->productMock->expects($this->once())->method('getAttributes') + ->willReturn([$attributeNonDate, $attributeDate]); + $this->productMock->expects($this->any())->method('getSku')->willReturn('sku'); + $this->productMock->expects($this->any())->method('getOptionsReadOnly')->willReturn(false); + + $customOptionMock = $this->getMockBuilder(Option::class) + ->disableOriginalConstructor() + ->setMethods(null) + ->getMock(); + $firstExpectedCustomOption = clone $customOptionMock; + $firstExpectedCustomOption->setData($optionsData['option2']); + $secondExpectedCustomOption = clone $customOptionMock; + $secondExpectedCustomOption->setData($optionsData['option3']); $this->customOptionFactoryMock->expects($this->any()) ->method('create') - ->with(['data' => $optionsData['option2']]) - ->willReturn($this->customOptionMock); - $this->productMock->expects($this->once()) - ->method('setOptions') - ->with([$this->customOptionMock]); + ->willReturnMap([ + [ + ['data' => $optionsData['option2']], + $firstExpectedCustomOption + ], [ + ['data' => $optionsData['option3']], + $secondExpectedCustomOption + ] + ]); + $website = $this->getMockBuilder(WebsiteInterface::class)->getMockForAbstractClass(); + $website->expects($this->any())->method('getId')->willReturn(1); + $this->storeManagerMock->expects($this->once())->method('isSingleStoreMode')->willReturn($isSingleStore); + $this->storeManagerMock->expects($this->any())->method('getWebsite')->willReturn($website); $this->assertEquals($this->productMock, $this->helper->initialize($this->productMock)); + $this->assertEquals($expWebsiteIds, $this->productMock->getDataByKey('website_ids')); + + $productOptions = $this->productMock->getOptions(); + $this->assertTrue(2 == count($productOptions)); + list($option2, $option3) = $productOptions; + $this->assertTrue($option2->getOptionId() == $optionsData['option2']['option_id']); + $this->assertTrue('sku' == $option2->getData('product_sku')); + $this->assertTrue($option3->getOptionId() == $optionsData['option3']['option_id']); + $this->assertTrue('sku' == $option2->getData('product_sku')); + } + + /** + * @return array + */ + public function initializeDataProvider() + { + return [ + [ + 'single_store' => false, + 'website_ids' => ['1' => 1, '2' => 1], + 'expected_website_ids' => ['1' => 1, '2' => 1] + ], + [ + 'single_store' => false, + 'website_ids' => ['1' => 1, '2' => 0], + 'expected_website_ids' => ['1' => 1] + ], + [ + 'single_store' => false, + 'website_ids' => ['1' => 0, '2' => 0], + 'expected_website_ids' => [] + ], + [ + 'single_store' => true, + 'website_ids' => [], + 'expected_website_ids' => ['1' => 1] + ], + ]; } /** @@ -362,9 +310,9 @@ public function mergeProductOptionsDataProvider() [ 'option_id' => '5', 'key1' => 'val1', - 'key2' => 'val2', + 'title' => 'val2', 'default_key1' => 'val3', - 'default_key2' => 'val4', + 'default_title' => 'val4', 'values' => [ [ 'option_type_id' => '2', @@ -379,7 +327,7 @@ public function mergeProductOptionsDataProvider() [ 5 => [ 'key1' => '0', - 'key2' => '1', + 'title' => '1', 'values' => [2 => ['key1' => 1]] ] ], @@ -387,9 +335,10 @@ public function mergeProductOptionsDataProvider() [ 'option_id' => '5', 'key1' => 'val1', - 'key2' => 'val4', + 'title' => 'val4', 'default_key1' => 'val3', - 'default_key2' => 'val4', + 'default_title' => 'val4', + 'is_delete_store_title' => 1, 'values' => [ [ 'option_type_id' => '2', @@ -413,8 +362,9 @@ public function mergeProductOptionsDataProvider() [ 'option_type_id' => '2', 'key1' => 'val1', - 'key2' => 'val2', - 'default_key1' => 'val11' + 'title' => 'val2', + 'default_key1' => 'val11', + 'default_title' => 'val22' ] ] ] @@ -423,7 +373,7 @@ public function mergeProductOptionsDataProvider() 7 => [ 'key1' => '1', 'key2' => '1', - 'values' => [2 => ['key1' => 1, 'key2' => 1]] + 'values' => [2 => ['key1' => 0, 'title' => 1]] ] ], [ @@ -435,9 +385,11 @@ public function mergeProductOptionsDataProvider() 'values' => [ [ 'option_type_id' => '2', - 'key1' => 'val11', - 'key2' => 'val2', - 'default_key1' => 'val11' + 'key1' => 'val1', + 'title' => 'val22', + 'default_key1' => 'val11', + 'default_title' => 'val22', + 'is_delete_store_title' => 1 ] ] ] diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/StockDataFilterTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/StockDataFilterTest.php index a226ce91b24d1..384db2dcb72b6 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/StockDataFilterTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/StockDataFilterTest.php @@ -1,6 +1,6 @@ priceProcessor = $this->getMockBuilder(\Magento\Catalog\Model\Indexer\Product\Price\Processor::class) + $this->priceProcessorMock = $this->getMockBuilder(Processor::class) ->disableOriginalConstructor()->getMock(); + $this->productBuilderMock = $this->getMockBuilder(Builder::class) + ->setMethods(['build']) + ->disableOriginalConstructor() + ->getMock(); - $productBuilder = $this->getMockBuilder( - \Magento\Catalog\Controller\Adminhtml\Product\Builder::class - )->setMethods(['build'])->disableOriginalConstructor()->getMock(); - - $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)->disableOriginalConstructor() - ->setMethods(['getTypeId', 'getStoreId', '__sleep', '__wakeup'])->getMock(); - $product->expects($this->any())->method('getTypeId')->will($this->returnValue('simple')); - $product->expects($this->any())->method('getStoreId')->will($this->returnValue('1')); - $productBuilder->expects($this->any())->method('build')->will($this->returnValue($product)); + $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['getTypeId', 'getStoreId', '__sleep', '__wakeup']) + ->getMock(); + $productMock->expects($this->any()) + ->method('getTypeId') + ->willReturn('simple'); + $productMock->expects($this->any()) + ->method('getStoreId') + ->willReturn('1'); + $this->productBuilderMock->expects($this->any()) + ->method('build') + ->willReturn($productMock); - $this->resultRedirect = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) + $this->resultRedirectMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) ->disableOriginalConstructor() ->getMock(); $resultFactory = $this->getMockBuilder(\Magento\Framework\Controller\ResultFactory::class) @@ -41,47 +83,71 @@ protected function setUp() $resultFactory->expects($this->atLeastOnce()) ->method('create') ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT) - ->willReturn($this->resultRedirect); + ->willReturn($this->resultRedirectMock); - $abstractDbMock = $this->getMockBuilder(\Magento\Framework\Data\Collection\AbstractDb::class) + $this->abstractDbMock = $this->getMockBuilder(AbstractDb::class) ->disableOriginalConstructor() ->setMethods(['getAllIds', 'getResource']) ->getMock(); - $abstractDbMock->expects($this->any()) - ->method('getAllIds') - ->willReturn([]); - - $filterMock = $this->getMockBuilder(\Magento\Ui\Component\MassAction\Filter::class) + $this->filterMock = $this->getMockBuilder(\Magento\Ui\Component\MassAction\Filter::class) ->disableOriginalConstructor() ->setMethods(['getCollection']) ->getMock(); - $filterMock->expects($this->any()) - ->method('getCollection') - ->willReturn($abstractDbMock); - - $collectionFactoryMock = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class - ) + $this->actionMock = $this->getMockBuilder(Action::class) + ->disableOriginalConstructor() + ->getMock(); + + $collectionFactoryMock = + $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); $collectionFactoryMock->expects($this->any()) ->method('create') - ->willReturn($abstractDbMock); + ->willReturn($this->abstractDbMock); + + $additionalParams = [ + 'resultFactory' => $resultFactory + ]; + /** @var \Magento\Backend\App\Action\Context $context */ + $context = $this->initContext($additionalParams, [[Action::class, $this->actionMock]]); - $additionalParams = ['resultFactory' => $resultFactory]; $this->action = new \Magento\Catalog\Controller\Adminhtml\Product\MassStatus( - $this->initContext($additionalParams), - $productBuilder, - $this->priceProcessor, - $filterMock, + $context, + $this->productBuilderMock, + $this->priceProcessorMock, + $this->filterMock, $collectionFactoryMock ); } public function testMassStatusAction() { - $this->priceProcessor->expects($this->once())->method('reindexList'); + $storeId = 1; + $status = \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED; + $filters = [ + 'store_id' => 2, + ]; + + $this->filterMock->expects($this->once()) + ->method('getCollection') + ->willReturn($this->abstractDbMock); + $this->abstractDbMock->expects($this->once()) + ->method('getAllIds') + ->willReturn([3]); + $this->request->expects($this->exactly(3)) + ->method('getParam') + ->willReturnMap([ + ['store', 0, $storeId], + ['status', null, $status], + ['filters', [], $filters] + ]); + $this->actionMock->expects($this->once()) + ->method('updateAttributes') + ->with([3], ['status' => $status], 2); + $this->priceProcessorMock->expects($this->once()) + ->method('reindexList'); + $this->action->execute(); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php index f8754ce00e50d..71f729ffec9d1 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php @@ -1,7 +1,7 @@ getMock(\Magento\Catalog\Model\Product\Action::class, [], [], '', false); - $objectManagerMock = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); - $objectManagerMock->expects($this->any())->method('get')->will($this->returnValue($productActionMock)); + + $this->objectManagerMock = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); + + if ($objectManagerMap) { + $this->objectManagerMock->expects($this->any()) + ->method('get') + ->willReturnMap($objectManagerMap); + } + + $this->objectManagerMock->expects($this->any()) + ->method('get') + ->willReturn($productActionMock); $block = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) ->disableOriginalConstructor()->getMockForAbstractClass(); @@ -93,7 +111,7 @@ protected function initContext(array $additionalParams = []) $this->context->expects($this->any())->method('getEventManager')->will($this->returnValue($eventManager)); $this->context->expects($this->any())->method('getRequest')->will($this->returnValue($requestInterfaceMock)); $this->context->expects($this->any())->method('getResponse')->will($this->returnValue($responseInterfaceMock)); - $this->context->expects($this->any())->method('getObjectManager')->will($this->returnValue($objectManagerMock)); + $this->context->expects($this->any())->method('getObjectManager')->willReturn($this->objectManagerMock); $this->context->expects($this->any())->method('getMessageManager') ->will($this->returnValue($managerInterfaceMock)); diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Category/MoveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Category/MoveTest.php new file mode 100644 index 0000000000000..035218ee57918 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Category/MoveTest.php @@ -0,0 +1,320 @@ +resultJsonFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\JsonFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->layoutFactoryMock = $this->getMockBuilder(\Magento\Framework\View\LayoutFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->context = $this->getMock(\Magento\Backend\App\Action\Context::class, [], [], '', false); + $this->loggerMock = $this->getMock(\Psr\Log\LoggerInterface::class); + $this->fillContext(); + + $this->moveController = new Move( + $this->context, + $this->resultJsonFactoryMock, + $this->layoutFactoryMock, + $this->loggerMock + ); + $this->initObjectManager(); + } + + private function fillContext() + { + $this->request = $this + ->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->setMethods(['getPost']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->context->expects($this->once())->method('getRequest')->will($this->returnValue($this->request)); + $this->messageManager = $this->getMock(ManagerInterface::class); + $this->context->expects($this->once())->method('getMessageManager')->willReturn($this->messageManager); + } + + private function initObjectManager() + { + $this->objectManager = $this->getMock(ObjectManagerInterface::class); + $moveController = new \ReflectionClass($this->moveController); + $objectManagerProp = $moveController->getProperty('_objectManager'); + $objectManagerProp->setAccessible(true); + $objectManagerProp->setValue($this->moveController, $this->objectManager); + } + + public function testExecuteWithGenericException() + { + $messagesCollection = $this->getMockBuilder(\Magento\Framework\Message\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $messageBlock = $this->getMockBuilder(\Magento\Framework\View\Element\Messages::class) + ->disableOriginalConstructor() + ->getMock(); + $layoutMock = $this->getMock(\Magento\Framework\View\LayoutInterface::class); + $this->layoutFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($layoutMock); + $layoutMock->expects($this->once()) + ->method('getMessagesBlock') + ->willReturn($messageBlock); + $wysiwigConfig = $this->getMockBuilder(\Magento\Cms\Model\Wysiwyg\Config::class) + ->disableOriginalConstructor() + ->getMock(); + $registry = $this->getMockBuilder(Registry::class) + ->disableOriginalConstructor() + ->getMock(); + $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + ->disableOriginalConstructor() + ->getMock(); + $this->request->expects($this->exactly(2)) + ->method('getPost') + ->withConsecutive(['pid', false], ['aid', false]) + ->willReturnMap([['pid', false, 2], ['aid', false, 1]]); + $this->objectManager->expects($this->once()) + ->method('create') + ->with(\Magento\Catalog\Model\Category::class) + ->willReturn($categoryMock); + $this->objectManager->expects($this->any()) + ->method('get') + ->withConsecutive([Registry::class], [Registry::class], [\Magento\Cms\Model\Wysiwyg\Config::class]) + ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwigConfig]]); + $categoryMock->expects($this->once()) + ->method('move') + ->willThrowException(new \Exception( + __('Some exception') + )); + $this->messageManager->expects($this->once()) + ->method('addError') + ->with(__('There was a category move error.')); + $this->messageManager->expects($this->once()) + ->method('getMessages') + ->with(true) + ->willReturn($messagesCollection); + $messageBlock->expects($this->once()) + ->method('setMessages') + ->with($messagesCollection); + $resultJsonMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) + ->disableOriginalConstructor() + ->getMock(); + $messageBlock->expects($this->once()) + ->method('getGroupedHtml') + ->willReturn(''); + $resultJsonMock->expects($this->once()) + ->method('setData') + ->with( + [ + 'messages' => '', + 'error' => true + ] + ) + ->willReturn(true); + $this->resultJsonFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($resultJsonMock); + $this->assertTrue($this->moveController->execute()); + } + + public function testExecuteWithLocaliedException() + { + $exceptionMessage = 'Sorry, but we can\'t find the new category you selected.'; + $messagesCollection = $this->getMockBuilder(\Magento\Framework\Message\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $messageBlock = $this->getMockBuilder(\Magento\Framework\View\Element\Messages::class) + ->disableOriginalConstructor() + ->getMock(); + $layoutMock = $this->getMock(\Magento\Framework\View\LayoutInterface::class); + $this->layoutFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($layoutMock); + $layoutMock->expects($this->once()) + ->method('getMessagesBlock') + ->willReturn($messageBlock); + $wysiwigConfig = $this->getMockBuilder(\Magento\Cms\Model\Wysiwyg\Config::class) + ->disableOriginalConstructor() + ->getMock(); + $registry = $this->getMockBuilder(Registry::class) + ->disableOriginalConstructor() + ->getMock(); + $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + ->disableOriginalConstructor() + ->getMock(); + $this->request->expects($this->exactly(2)) + ->method('getPost') + ->withConsecutive(['pid', false], ['aid', false]) + ->willReturnMap([['pid', false, 2], ['aid', false, 1]]); + $this->objectManager->expects($this->once()) + ->method('create') + ->with(\Magento\Catalog\Model\Category::class) + ->willReturn($categoryMock); + $this->objectManager->expects($this->any()) + ->method('get') + ->withConsecutive([Registry::class], [Registry::class], [\Magento\Cms\Model\Wysiwyg\Config::class]) + ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwigConfig]]); + $this->messageManager->expects($this->once()) + ->method('addError') + ->with($exceptionMessage); + $this->messageManager->expects($this->once()) + ->method('getMessages') + ->with(true) + ->willReturn($messagesCollection); + $messageBlock->expects($this->once()) + ->method('setMessages') + ->with($messagesCollection); + $resultJsonMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) + ->disableOriginalConstructor() + ->getMock(); + $messageBlock->expects($this->once()) + ->method('getGroupedHtml') + ->willReturn(''); + $resultJsonMock->expects($this->once()) + ->method('setData') + ->with( + [ + 'messages' => '', + 'error' => true + ] + ) + ->willReturn(true); + $categoryMock->expects($this->once()) + ->method('move') + ->willThrowException(new \Magento\Framework\Exception\LocalizedException( + __($exceptionMessage) + )); + $this->resultJsonFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($resultJsonMock); + $this->assertTrue($this->moveController->execute()); + } + + public function testSuccessfullCategorySave() + { + $messagesCollection = $this->getMockBuilder(\Magento\Framework\Message\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $messageBlock = $this->getMockBuilder(\Magento\Framework\View\Element\Messages::class) + ->disableOriginalConstructor() + ->getMock(); + $layoutMock = $this->getMock(\Magento\Framework\View\LayoutInterface::class); + $this->layoutFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($layoutMock); + $layoutMock->expects($this->once()) + ->method('getMessagesBlock') + ->willReturn($messageBlock); + $wysiwigConfig = $this->getMockBuilder(\Magento\Cms\Model\Wysiwyg\Config::class) + ->disableOriginalConstructor() + ->getMock(); + $registry = $this->getMockBuilder(Registry::class) + ->disableOriginalConstructor() + ->getMock(); + $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + ->disableOriginalConstructor() + ->getMock(); + $this->request->expects($this->exactly(2)) + ->method('getPost') + ->withConsecutive(['pid', false], ['aid', false]) + ->willReturnMap([['pid', false, 2], ['aid', false, 1]]); + $this->objectManager->expects($this->once()) + ->method('create') + ->with(\Magento\Catalog\Model\Category::class) + ->willReturn($categoryMock); + $this->objectManager->expects($this->any()) + ->method('get') + ->withConsecutive([Registry::class], [Registry::class], [\Magento\Cms\Model\Wysiwyg\Config::class]) + ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwigConfig]]); + $this->messageManager->expects($this->once()) + ->method('getMessages') + ->with(true) + ->willReturn($messagesCollection); + $messageBlock->expects($this->once()) + ->method('setMessages') + ->with($messagesCollection); + $resultJsonMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) + ->disableOriginalConstructor() + ->getMock(); + $messageBlock->expects($this->once()) + ->method('getGroupedHtml') + ->willReturn(''); + $resultJsonMock->expects($this->once()) + ->method('setData') + ->with( + [ + 'messages' => '', + 'error' => false + ] + ) + ->willReturn(true); + $this->messageManager->expects($this->once()) + ->method('addSuccess') + ->with(__('You moved the category.')); + $categoryMock->expects($this->once()) + ->method('move') + ->with(2, 1); + $this->resultJsonFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($resultJsonMock); + $this->assertTrue($this->moveController->execute()); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php index d8c18300a31f2..de475da82d206 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php @@ -1,6 +1,6 @@ image->expects($this->any()) ->method('isBaseFilePlaceholder') ->willReturn($isBaseFilePlaceholder); - $this->image->expects($this->any()) - ->method('getNewFile') - ->willReturn($newFile); $this->prepareAttributes([], $imageId); @@ -502,7 +497,6 @@ public function getResizedImageInfoDataProvider() 'image_id' => 'test_image_id', 'image_file' => '/path/to/test_image_id.png', 'base_file' => '/path/to/base_image.png', - 'new_file' => '/path/to/base_image.png', 'destination' => 'small_image', 'set_image_file' => true, 'is_cached' => false, @@ -516,7 +510,6 @@ public function getResizedImageInfoDataProvider() 'image_id' => 'test_image_id', 'image_file' => '/path/to/test_image_id.png', 'base_file' => null, - 'new_file' => true, 'destination' => 'small_image', 'set_image_file' => false, 'is_cached' => false, @@ -530,7 +523,6 @@ public function getResizedImageInfoDataProvider() 'image_id' => 'test_image_id', 'image_file' => '/path/to/test_image_id.png', 'base_file' => null, - 'new_file' => false, 'destination' => 'small_image', 'set_image_file' => true, 'is_cached' => false, @@ -544,7 +536,6 @@ public function getResizedImageInfoDataProvider() 'image_id' => 'test_image_id', 'image_file' => '/path/to/test_image_id.png', 'base_file' => null, - 'new_file' => true, 'destination' => 'small_image', 'set_image_file' => true, 'is_cached' => false, @@ -558,7 +549,6 @@ public function getResizedImageInfoDataProvider() 'image_id' => 'test_image_id', 'image_file' => '/path/to/test_image_id.png', 'base_file' => null, - 'new_file' => '/path/to/test_image_id.png', 'destination' => 'small_image', 'set_image_file' => true, 'is_cached' => false, diff --git a/app/code/Magento/Catalog/Test/Unit/Helper/Product/CompareTest.php b/app/code/Magento/Catalog/Test/Unit/Helper/Product/CompareTest.php index 29786ecdce969..997db3a409b7f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Helper/Product/CompareTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Helper/Product/CompareTest.php @@ -1,6 +1,6 @@ strtr(base64_encode($compareListUrl), '+/=', '-_,'), - 'product' => $productId + Action::PARAM_NAME_URL_ENCODED => '', + 'product' => $productId, + 'confirmation' => true, + 'confirmationMessage' => __('Are you sure you want to remove this item from your Compare Products list?'), ]; //Verification - $this->urlBuilder->expects($this->at(0)) - ->method('getUrl') - ->with($compareListUrl) - ->will($this->returnValue($compareListUrl)); - $this->urlBuilder->expects($this->at(1)) + $this->urlBuilder->expects($this->once()) ->method('getUrl') ->with($removeUrl) ->will($this->returnValue($removeUrl)); @@ -159,18 +156,14 @@ public function testGetClearListUrl() public function testGetPostDataClearList() { //Data - $refererUrl = 'home/'; $clearUrl = 'catalog/product_compare/clear'; $postParams = [ - Action::PARAM_NAME_URL_ENCODED => strtr(base64_encode($refererUrl), '+/=', '-_,') + Action::PARAM_NAME_URL_ENCODED => '', + 'confirmation' => true, + 'confirmationMessage' => __('Are you sure you want to remove all items from your Compare Products list?'), ]; //Verification - $this->request->expects($this->once()) - ->method('getServer') - ->with('HTTP_REFERER') - ->will($this->returnValue($refererUrl)); - $this->urlBuilder->expects($this->once()) ->method('getUrl') ->with($clearUrl) diff --git a/app/code/Magento/Catalog/Test/Unit/Helper/Product/ConfigurationPoolTest.php b/app/code/Magento/Catalog/Test/Unit/Helper/Product/ConfigurationPoolTest.php index f7ef5095529ec..a852d5ab3e864 100644 --- a/app/code/Magento/Catalog/Test/Unit/Helper/Product/ConfigurationPoolTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Helper/Product/ConfigurationPoolTest.php @@ -1,6 +1,6 @@ getMock(\Magento\Framework\App\Helper\Context::class, [], [], '', false); + $optionFactoryMock = $this->getMock(\Magento\Catalog\Model\Product\OptionFactory::class, [], [], '', false); + $filterManagerMock = $this->getMock(\Magento\Framework\Filter\FilterManager::class, [], [], '', false); + $stringUtilsMock = $this->getMock(\Magento\Framework\Stdlib\StringUtils::class, [], [], '', false); + $this->serializer = $this->getMock(\Magento\Framework\Serialize\Serializer\Json::class, [], [], '', false); + + $this->helper = new \Magento\Catalog\Helper\Product\Configuration( + $contextMock, + $optionFactoryMock, + $filterManagerMock, + $stringUtilsMock, + $this->serializer + ); + } + + /** + * Retrieves product additional options + */ + public function testGetAdditionalOptionOnly() + { + $additionalOptionResult = ['additional_option' => 1]; + + $itemMock = $this->getMock( + \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface::class, + [], + [], + '', + false + ); + $optionMock = $this->getMock( + \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface::class, + [], + [], + '', + false + ); + $additionalOptionMock = $this->getMock( + \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface::class, + [], + [], + '', + false + ); + $productMock = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); + + $this->serializer->expects($this->once())->method('unserialize')->willReturn($additionalOptionResult); + $optionMock->expects($this->once())->method('getValue')->willReturn(null); + $additionalOptionMock->expects($this->once())->method('getValue'); + + $itemMock->expects($this->once())->method('getProduct')->willReturn($productMock); + $itemMock->expects($this->any())->method('getOptionByCode')->will($this->returnValueMap( + [ + ['option_ids', $optionMock], + ['additional_options', $additionalOptionMock] + ] + )); + + $this->assertEquals($additionalOptionResult, $this->helper->getCustomOptions($itemMock)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Helper/Product/Edit/Action/AttributeTest.php b/app/code/Magento/Catalog/Test/Unit/Helper/Product/Edit/Action/AttributeTest.php index a6b11d7993950..04faa6bdf03d8 100644 --- a/app/code/Magento/Catalog/Test/Unit/Helper/Product/Edit/Action/AttributeTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Helper/Product/Edit/Action/AttributeTest.php @@ -1,6 +1,6 @@ ['test_attribute'], 'group_two' => ['attribute_one', 'attribute_two']]; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_merged.xml b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_merged.xml index 131fe397f2e6b..813e9d64af710 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_merged.xml +++ b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_merged.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_one.xml b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_one.xml index 3e11d226e6af4..3fe4cc449c51d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_one.xml +++ b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_one.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_two.xml b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_two.xml index 772a85eafe95e..718895e7117fb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_two.xml +++ b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Config/_files/attributes_config_two.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/ConfigTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/ConfigTest.php index 358a5ed67e63d..64a26b7cd8664 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/ConfigTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/ConfigTest.php @@ -1,6 +1,6 @@ objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->attribute = $this->getMockForAbstractClass( + \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, + [], + 'TestAttribute', + false, + false, + true, + ['getName'] + ); + + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + + $this->logger = $this->getMockForAbstractClass( + \Psr\Log\LoggerInterface::class, + [], + 'TestLogger', + false, + false, + true, + ['critical'] + ); + + $this->imageUploader = $this->getMock( + \Magento\Catalog\Model\ImageUploader::class, + ['moveFileFromTmp'], + [], + '', + false + ); + } + + /** + * @return array + */ + public function deletedValueDataProvider() + { + return [ + [false], + [['delete' => true]] + ]; + } + + /** + * @dataProvider deletedValueDataProvider + * + * @param array $value + */ + public function testBeforeSaveValueDeletion($value) + { + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); + $model->setAttribute($this->attribute); + + $object = new \Magento\Framework\DataObject([ + 'test_attribute' => $value + ]); + + $model->beforeSave($object); + + $this->assertEquals('', $object->getTestAttribute()); + } + + /** + * @return array + */ + public function invalidValueDataProvider() + { + $closure = function () { + return false; + }; + + return [ + [1234], + [true], + [new \stdClass()], + [$closure], + [['a' => 1, 'b' => 2]] + ]; + } + + /** + * @dataProvider invalidValueDataProvider + * + * @param array $value + */ + public function testBeforeSaveValueInvalid($value) + { + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); + $model->setAttribute($this->attribute); + + $object = new \Magento\Framework\DataObject([ + 'test_attribute' => $value + ]); + + $model->beforeSave($object); + + $this->assertEquals('', $object->getTestAttribute()); + } + + public function testBeforeSaveAttributeFileName() + { + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); + $model->setAttribute($this->attribute); + + $object = new \Magento\Framework\DataObject([ + 'test_attribute' => [ + ['name' => 'test123.jpg'] + ] + ]); + + $model->beforeSave($object); + + $this->assertEquals('test123.jpg', $object->getTestAttribute()); + } + + public function testBeforeSaveTemporaryAttribute() + { + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); + $model->setAttribute($this->attribute); + + $object = new \Magento\Framework\DataObject([ + 'test_attribute' => [ + ['name' => 'test123.jpg', 'tmp_name' => 'abc123', 'url' => 'http://www.example.com/test123.jpg'] + ] + ]); + + $model->beforeSave($object); + + $this->assertEquals([ + ['name' => 'test123.jpg', 'tmp_name' => 'abc123', 'url' => 'http://www.example.com/test123.jpg'] + ], $object->getData('_additional_data_test_attribute')); + } + + public function testBeforeSaveAttributeStringValue() + { + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); + $model->setAttribute($this->attribute); + + $object = new \Magento\Framework\DataObject([ + 'test_attribute' => 'test123.jpg' + ]); + + $model->beforeSave($object); + + $this->assertEquals('test123.jpg', $object->getTestAttribute()); + $this->assertNull($object->getData('_additional_data_test_attribute')); + } + + /** + * @return \Magento\Catalog\Model\Category\Attribute\Backend\Image + */ + private function setUpModelForAfterSave() + { + $objectManagerMock = $this->getMock( + \Magento\Framework\App\ObjectManager::class, + ['get'], + [], + '', + false + ); + + $imageUploaderMock = $this->imageUploader; + + $objectManagerMock->expects($this->any()) + ->method('get') + ->will($this->returnCallback(function ($class, $params = []) use ($imageUploaderMock) { + if ($class == \Magento\Catalog\CategoryImageUpload::class) { + return $imageUploaderMock; + } + + return $this->objectManager->get($class, $params); + })); + + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class, [ + 'objectManager' => $objectManagerMock, + 'logger' => $this->logger + ]); + $this->objectManager->setBackwardCompatibleProperty($model, 'imageUploader', $this->imageUploader); + + return $model->setAttribute($this->attribute); + } + + public function attributeValueDataProvider() + { + return [ + [[['name' => 'test1234.jpg']]], + ['test1234.jpg'], + [''], + [false] + ]; + } + + /** + * @dataProvider attributeValueDataProvider + * + * @param array $value + */ + public function testAfterSaveWithAdditionalData($value) + { + $model = $this->setUpModelForAfterSave(); + + $this->imageUploader->expects($this->once()) + ->method('moveFileFromTmp') + ->with($this->equalTo('test1234.jpg')); + + $object = new \Magento\Framework\DataObject( + [ + 'test_attribute' => $value, + '_additional_data_test_attribute' => [['name' => 'test1234.jpg']] + ] + ); + + $model->afterSave($object); + } + + /** + * @dataProvider attributeValueDataProvider + * + * @param array $value + */ + public function testAfterSaveWithoutAdditionalData($value) + { + $model = $this->setUpModelForAfterSave(); + + $this->imageUploader->expects($this->never()) + ->method('moveFileFromTmp'); + + $object = new \Magento\Framework\DataObject( + [ + 'test_attribute' => $value + ] + ); + + $model->afterSave($object); + } + + public function testAfterSaveWithExceptions() + { + $model = $this->setUpModelForAfterSave(); + + $exception = new \Exception(); + + $this->imageUploader->expects($this->any()) + ->method('moveFileFromTmp') + ->will($this->throwException($exception)); + + $this->logger->expects($this->once()) + ->method('critical') + ->with($this->equalTo($exception)); + + $object = new \Magento\Framework\DataObject( + [ + '_additional_data_test_attribute' => [['name' => 'test1234.jpg']] + ] + ); + + $model->afterSave($object); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php index b720856d8554d..62c0fc6d9f535 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/SortbyTest.php @@ -1,6 +1,6 @@ eavValidationRules = $this->getMockBuilder(EavValidationRules::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->collection = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->collection->expects($this->any()) + ->method('addAttributeToSelect') + ->with('*') + ->willReturnSelf(); + + $this->categoryCollectionFactory = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->categoryCollectionFactory->expects($this->any()) + ->method('create') + ->willReturn($this->collection); + + $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) + ->getMockForAbstractClass(); + + $this->registry = $this->getMockBuilder(Registry::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->eavEntityMock = $this->getMockBuilder(Type::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->eavConfig = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->request = $this->getMockBuilder(RequestInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->categoryFactory = $this->getMockBuilder(CategoryFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileInfo = $this->getMockBuilder(FileInfo::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @return DataProvider + */ + private function getModel() + { + $this->eavEntityMock->expects($this->any()) + ->method('getAttributeCollection') + ->willReturn([]); + + $this->eavConfig->expects($this->any()) + ->method('getEntityType') + ->with('catalog_category') + ->willReturn($this->eavEntityMock); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $model = $objectManager->getObject( + DataProvider::class, + [ + 'eavValidationRules' => $this->eavValidationRules, + 'categoryCollectionFactory' => $this->categoryCollectionFactory, + 'storeManager' => $this->storeManager, + 'registry' => $this->registry, + 'eavConfig' => $this->eavConfig, + 'request' => $this->request, + 'categoryFactory' => $this->categoryFactory, + ] + ); + + $objectManager->setBackwardCompatibleProperty( + $model, + 'fileInfo', + $this->fileInfo + ); + + return $model; + } + + public function testGetDataNoCategory() + { + $this->registry->expects($this->once()) + ->method('registry') + ->with('category') + ->willReturn(null); + + $model = $this->getModel(); + $this->assertNull($model->getData()); + } + + public function testGetDataNoFileExists() + { + $fileName = 'filename.ext1'; + $categoryId = 1; + + $categoryData = [ + 'image' => $fileName, + ]; + + $imageBackendMock = $this->getMockBuilder(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class) + ->disableOriginalConstructor() + ->getMock(); + + $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeMock->expects($this->once()) + ->method('getBackend') + ->willReturn($imageBackendMock); + + $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + ->disableOriginalConstructor() + ->getMock(); + $categoryMock->expects($this->exactly(2)) + ->method('getData') + ->willReturnMap([ + ['', null, $categoryData], + ['image', null, $categoryData['image']], + ]); + $categoryMock->expects($this->any()) + ->method('getExistsStoreValueFlag') + ->with('url_key') + ->willReturn(false); + $categoryMock->expects($this->any()) + ->method('getStoreId') + ->willReturn(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $categoryMock->expects($this->once()) + ->method('getId') + ->willReturn($categoryId); + $categoryMock->expects($this->once()) + ->method('getAttributes') + ->willReturn(['image' => $attributeMock]); + + $this->registry->expects($this->once()) + ->method('registry') + ->with('category') + ->willReturn($categoryMock); + + $this->fileInfo->expects($this->once()) + ->method('isExist') + ->with($fileName) + ->willReturn(false); + + $model = $this->getModel(); + $result = $model->getData(); + + $this->assertTrue(is_array($result)); + $this->assertArrayHasKey($categoryId, $result); + $this->assertArrayNotHasKey('image', $result[$categoryId]); + } + + public function testGetData() + { + $fileName = 'filename.png'; + $mime = 'image/png'; + $size = 1; + + $categoryId = 1; + $categoryUrl = 'category_url'; + + $categoryData = [ + 'image' => $fileName, + ]; + + $expects = [ + [ + 'name' => $fileName, + 'url' => $categoryUrl, + 'size' => $size, + 'type' => $mime, + ], + ]; + + $imageBackendMock = $this->getMockBuilder(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class) + ->disableOriginalConstructor() + ->getMock(); + + $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeMock->expects($this->once()) + ->method('getBackend') + ->willReturn($imageBackendMock); + + $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + ->disableOriginalConstructor() + ->getMock(); + $categoryMock->expects($this->exactly(2)) + ->method('getData') + ->willReturnMap([ + ['', null, $categoryData], + ['image', null, $categoryData['image']], + ]); + $categoryMock->expects($this->any()) + ->method('getExistsStoreValueFlag') + ->with('url_key') + ->willReturn(false); + $categoryMock->expects($this->any()) + ->method('getStoreId') + ->willReturn(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $categoryMock->expects($this->once()) + ->method('getId') + ->willReturn($categoryId); + $categoryMock->expects($this->once()) + ->method('getAttributes') + ->willReturn(['image' => $attributeMock]); + $categoryMock->expects($this->once()) + ->method('getImageUrl') + ->willReturn($categoryUrl); + + $this->registry->expects($this->once()) + ->method('registry') + ->with('category') + ->willReturn($categoryMock); + + $this->fileInfo->expects($this->once()) + ->method('isExist') + ->with($fileName) + ->willReturn(true); + $this->fileInfo->expects($this->once()) + ->method('getStat') + ->with($fileName) + ->willReturn(['size' => $size]); + $this->fileInfo->expects($this->once()) + ->method('getMimeType') + ->with($fileName) + ->willReturn($mime); + + $model = $this->getModel(); + $result = $model->getData(); + + $this->assertTrue(is_array($result)); + $this->assertArrayHasKey($categoryId, $result); + $this->assertArrayHasKey('image', $result[$categoryId]); + + $this->assertEquals($expects, $result[$categoryId]['image']); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php new file mode 100644 index 0000000000000..973fa8555264a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php @@ -0,0 +1,114 @@ +mediaDirectory = $this->getMockBuilder(WriteInterface::class) + ->getMockForAbstractClass(); + + $this->filesystem = $this->getMockBuilder(Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + $this->filesystem->expects($this->any()) + ->method('getDirectoryWrite') + ->with(DirectoryList::MEDIA) + ->willReturn($this->mediaDirectory); + + $this->mime = $this->getMockBuilder(Mime::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new FileInfo( + $this->filesystem, + $this->mime + ); + } + + public function testGetMimeType() + { + $mediaPath = '/catalog/category'; + + $fileName = '/filename.ext1'; + $absoluteFilePath = '/absolute_path/catalog/category/filename.ext1'; + + $expected = 'ext1'; + + $this->mediaDirectory->expects($this->once()) + ->method('getAbsolutePath') + ->with($mediaPath. '/' . ltrim($fileName, '/')) + ->willReturn($absoluteFilePath); + + $this->mime->expects($this->once()) + ->method('getMimeType') + ->with($absoluteFilePath) + ->willReturn($expected); + + $this->assertEquals($expected, $this->model->getMimeType($fileName)); + } + + public function testGetStat() + { + $mediaPath = '/catalog/category'; + + $fileName = '/filename.ext1'; + + $expected = ['size' => 1]; + + $this->mediaDirectory->expects($this->once()) + ->method('stat') + ->with($mediaPath . $fileName) + ->willReturn($expected); + + $result = $this->model->getStat($fileName); + + $this->assertTrue(is_array($result)); + $this->assertArrayHasKey('size', $result); + $this->assertEquals(1, $result['size']); + } + + public function testIsExist() + { + $mediaPath = '/catalog/category'; + + $fileName = '/filename.ext1'; + + $this->mediaDirectory->expects($this->once()) + ->method('isExist') + ->with($mediaPath . $fileName) + ->willReturn(true); + + $this->assertTrue($this->model->isExist($fileName)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Link/ReadHandlerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Link/ReadHandlerTest.php index 8be5ca3dba1ef..8d1068fb5172e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Link/ReadHandlerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Link/ReadHandlerTest.php @@ -1,6 +1,6 @@ context = $this->getMock( - \Magento\Framework\Model\Context::class, - ['getEventDispatcher', 'getCacheManager'], + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->registry = $this->getMock(\Magento\Framework\Registry::class); + $this->storeManager = $this->getMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->categoryTreeResource = $this->getMock( + \Magento\Catalog\Model\ResourceModel\Category\Tree::class, + [], [], '', false ); - - $this->eventManager = $this->getMock(\Magento\Framework\Event\ManagerInterface::class); - $this->context->expects($this->any())->method('getEventDispatcher') - ->will($this->returnValue($this->eventManager)); - $this->cacheManager = $this->getMock(\Magento\Framework\App\CacheInterface::class); - $this->context->expects($this->any())->method('getCacheManager') - ->will($this->returnValue($this->cacheManager)); - - $this->registry = $this->getMock(\Magento\Framework\Registry::class); - $this->storeManager = $this->getMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->categoryTreeResource = $this->getMock( - \Magento\Catalog\Model\ResourceModel\Category\Tree::class, - [], - [], - '', - false - ); - $this->categoryTreeFactory = $this->getMock( + $this->categoryTreeFactory = $this->getMock( \Magento\Catalog\Model\ResourceModel\Category\TreeFactory::class, ['create'], [], '', false); $this->categoryRepository = $this->getMock(\Magento\Catalog\Api\CategoryRepositoryInterface::class); - $this->storeCollectionFactory = $this->getMock( + $this->storeCollectionFactory = $this->getMock( \Magento\Store\Model\ResourceModel\Store\CollectionFactory::class, ['create'], [], @@ -130,7 +149,7 @@ protected function setUp() false ); $this->url = $this->getMock(\Magento\Framework\UrlInterface::class); - $this->productCollectionFactory = $this->getMock( + $this->productCollectionFactory = $this->getMock( \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class, ['create'], [], @@ -138,7 +157,7 @@ protected function setUp() false ); $this->catalogConfig = $this->getMock(\Magento\Catalog\Model\Config::class, [], [], '', false); - $this->filterManager = $this->getMock( + $this->filterManager = $this->getMock( \Magento\Framework\Filter\FilterManager::class, ['translitUrl'], [], @@ -148,7 +167,7 @@ protected function setUp() $this->flatState = $this->getMock(\Magento\Catalog\Model\Indexer\Category\Flat\State::class, [], [], '', false); $this->flatIndexer = $this->getMock(\Magento\Framework\Indexer\IndexerInterface::class); $this->productIndexer = $this->getMock(\Magento\Framework\Indexer\IndexerInterface::class); - $this->categoryUrlPathGenerator = $this->getMock( + $this->categoryUrlPathGenerator = $this->getMock( \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator::class, [], [], @@ -156,19 +175,19 @@ protected function setUp() false ); $this->urlFinder = $this->getMock(\Magento\UrlRewrite\Model\UrlFinderInterface::class); - $this->resource = $this->getMock( + $this->resource = $this->getMock( \Magento\Catalog\Model\ResourceModel\Category::class, [], [], '', false ); - $this->indexerRegistry = $this->getMock( - \Magento\Framework\Indexer\IndexerRegistry::class, - ['get'], - [], - '', - false + $this->indexerRegistry = $this->getMock( + \Magento\Framework\Indexer\IndexerRegistry::class, + ['get'], + [], + '', + false ); $this->metadataServiceMock = $this->getMock(\Magento\Catalog\Api\CategoryAttributeRepositoryInterface::class); @@ -198,7 +217,7 @@ public function testFormatUrlKey() public function testMoveWhenCannotFindParentCategory() { $this->markTestIncomplete('MAGETWO-31165'); - $parentCategory = $this->getMock( + $parentCategory = $this->getMock( \Magento\Catalog\Model\Category::class, ['getId', 'setStoreId', 'load'], [], @@ -223,7 +242,7 @@ public function testMoveWhenCannotFindParentCategory() */ public function testMoveWhenCannotFindNewCategory() { - $parentCategory = $this->getMock( + $parentCategory = $this->getMock( \Magento\Catalog\Model\Category::class, ['getId', 'setStoreId', 'load'], [], @@ -250,7 +269,7 @@ public function testMoveWhenCannotFindNewCategory() public function testMoveWhenParentCategoryIsSameAsChildCategory() { $this->markTestIncomplete('MAGETWO-31165'); - $parentCategory = $this->getMock( + $parentCategory = $this->getMock( \Magento\Catalog\Model\Category::class, ['getId', 'setStoreId', 'load'], [], @@ -277,7 +296,7 @@ public function testMovePrimaryWorkflow() ->method('get') ->with('catalog_category_product') ->will($this->returnValue($indexer)); - $parentCategory = $this->getMock( + $parentCategory = $this->getMock( \Magento\Catalog\Model\Category::class, ['getId', 'setStoreId', 'load'], [], @@ -313,10 +332,9 @@ public function testGetUseFlatResourceTrue() protected function getCategoryModel() { - return (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( + return $this->objectManager->getObject( \Magento\Catalog\Model\Category::class, [ - 'context' => $this->context, 'registry' => $this->registry, 'storeManager' => $this->storeManager, 'categoryTreeResource' => $this->categoryTreeResource, @@ -487,4 +505,76 @@ public function testGetCustomAttributes() $this->category->getCustomAttribute($descriptionAttributeCode)->getValue() ); } + + /** + * @return array + */ + public function getImageWithAttributeCodeDataProvider() + { + return [ + ['testimage', 'http://www.example.com/catalog/category/testimage'], + [false, false] + ]; + } + + /** + * @param string|bool $value + * @param string|bool $url + * + * @dataProvider getImageWithAttributeCodeDataProvider + */ + public function testGetImageWithAttributeCode($value, $url) + { + $storeManager = $this->getMock(\Magento\Store\Model\StoreManager::class, ['getStore'], [], '', false); + $store = $this->getMock(\Magento\Store\Model\Store::class, ['getBaseUrl'], [], '', false); + + $storeManager->expects($this->any()) + ->method('getStore') + ->will($this->returnValue($store)); + + $store->expects($this->any()) + ->method('getBaseUrl') + ->with(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA) + ->will($this->returnValue('http://www.example.com/')); + + /** @var \Magento\Catalog\Model\Category $model */ + $model = $this->objectManager->getObject( + \Magento\Catalog\Model\Category::class, + [ + 'storeManager' => $storeManager + ] + ); + + $model->setData('attribute1', $value); + + $result = $model->getImageUrl('attribute1'); + + $this->assertEquals($url, $result); + } + + public function testGetImageWithoutAttributeCode() + { + $storeManager = $this->getMock(\Magento\Store\Model\StoreManager::class, ['getStore'], [], '', false); + $store = $this->getMock(\Magento\Store\Model\Store::class, ['getBaseUrl'], [], '', false); + + $storeManager->expects($this->any()) + ->method('getStore') + ->will($this->returnValue($store)); + + $store->expects($this->any()) + ->method('getBaseUrl') + ->with(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA) + ->will($this->returnValue('http://www.example.com/')); + + /** @var \Magento\Catalog\Model\Category $model */ + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category::class, [ + 'storeManager' => $storeManager + ]); + + $model->setData('image', 'myimage'); + + $result = $model->getImageUrl(); + + $this->assertEquals('http://www.example.com/catalog/category/myimage', $result); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php new file mode 100644 index 0000000000000..b543c92ac0875 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php @@ -0,0 +1,110 @@ +productMock = $this->getMock(Product::class, [], [], '', false, false); + $this->converterPoolMock = $this->getMock(ConverterPool::class, [], [], '', false, false); + $this->providerMock = $this->getMock(CollectionProviderInterface::class); + $this->converterMock = $this->getMock(ConverterInterface::class); + + $this->model = new CollectionProvider($this->converterPoolMock, ['crosssell' => $this->providerMock]); + } + + /** + * Test sort order of linked products based on configured item position. + */ + public function testGetCollection() + { + $linkedProductOneMock = $this->getMock(Product::class, [], [], '', false, false); + $linkedProductTwoMock = $this->getMock(Product::class, [], [], '', false, false); + $linkedProductThreeMock = $this->getMock(Product::class, [], [], '', false, false); + + $linkedProductOneMock->expects($this->once())->method('getId')->willReturn(1); + $linkedProductTwoMock->expects($this->once())->method('getId')->willReturn(2); + $linkedProductThreeMock->expects($this->once())->method('getId')->willReturn(3); + + $this->converterPoolMock->expects($this->once()) + ->method('getConverter') + ->with('crosssell') + ->willReturn($this->converterMock); + + $map = [ + [$linkedProductOneMock, ['name' => 'Product One', 'position' => 10]], + [$linkedProductTwoMock, ['name' => 'Product Two', 'position' => 2]], + [$linkedProductThreeMock, ['name' => 'Product Three', 'position' => 2]], + ]; + + $this->converterMock->expects($this->exactly(3))->method('convert')->willReturnMap($map); + + $this->providerMock->expects($this->once()) + ->method('getLinkedProducts') + ->with($this->productMock) + ->willReturn( + [ + $linkedProductOneMock, + $linkedProductTwoMock, + $linkedProductThreeMock + ] + ); + + $expectedResult = [ + 2 => ['name' => 'Product Two', 'position' => 2], + 3 => ['name' => 'Product Three', 'position' => 2], + 10 => ['name' => 'Product One', 'position' => 10], + ]; + + $actualResult = $this->model->getCollection($this->productMock, 'crosssell'); + + $this->assertEquals($expectedResult, $actualResult, 'Sort order of linked products in incorrect'); + } + + /** + * Test exception when collection provider is not configured for product link type. + * + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage Collection provider is not registered + */ + public function testGetCollectionWithMissingProviders() + { + $this->model->getCollection($this->productMock, 'upsell'); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php index 6a0d695d71f39..fb648ba03253a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php @@ -1,6 +1,6 @@ objectFactory = $this->getMockBuilder(\Magento\Framework\DataObject\Factory::class) @@ -90,12 +93,16 @@ protected function setUp() $this->buyRequest = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->setMethods(['unserialize']) + ->getMockForAbstractClass(); $this->processor = new CustomOptionProcessor( $this->objectFactory, $this->productOptionFactory, $this->extensionFactory, - $this->customOptionFactory + $this->customOptionFactory, + $this->serializer ); } @@ -126,6 +133,9 @@ public function testConvertToBuyRequest() $this->assertSame($this->buyRequest, $this->processor->convertToBuyRequest($this->cartItem)); } + /** + * @covers \Magento\Catalog\Model\CustomOptions\CustomOptionProcessor::getOptions() + */ public function testProcessCustomOptions() { $optionId = 23; @@ -136,9 +146,12 @@ public function testProcessCustomOptions() ->method('getOptionByCode') ->with('info_buyRequest') ->willReturn($quoteItemOption); - $quoteItemOption->expects($this->once()) + $quoteItemOption->expects($this->any()) ->method('getValue') - ->willReturn('a:1:{s:7:"options";a:1:{i:' . $optionId . ';a:2:{i:0;s:1:"5";i:1;s:1:"6";}}} '); + ->willReturn('{"options":{"' . $optionId . '":["5","6"]}}'); + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturn(json_decode($quoteItemOption->getValue(), true)); $this->customOptionFactory->expects($this->once()) ->method('create') ->willReturn($this->customOption); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php index d7a0fdb778d4c..f6fbbab61f8a0 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php @@ -1,6 +1,6 @@ flatIndexerMock = $this->getMockBuilder(\Magento\Catalog\Helper\Product\Flat\Indexer::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->tableDataMock = $this->getMockBuilder( + \Magento\Catalog\Model\Indexer\Product\Flat\TableDataInterface::class + )->disableOriginalConstructor()->getMockForAbstractClass(); + $this->connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->metadataPoolMock = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) + ->disableOriginalConstructor() + ->getMock(); + $this->metadataMock = $this->getMockBuilder( + \Magento\Framework\EntityManager\EntityMetadataInterface::class + )->disableOriginalConstructor()->getMockForAbstractClass(); + $this->metadataMock->expects($this->any())->method('getLinkField')->willReturn('entity_id'); + + $this->flatTableBuilder = $objectManagerHelper->getObject( + \Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder::class, + [ + 'productIndexerHelper' => $this->flatIndexerMock, + 'resource' => $this->resourceMock, + 'config' => $this->scopeConfigMock, + 'storeManager' => $this->storeManagerMock, + 'tableData' => $this->tableDataMock, + '_connection' => $this->connectionMock + ] + ); + $objectManagerHelper->setBackwardCompatibleProperty( + $this->flatTableBuilder, + 'metadataPool', + $this->metadataPoolMock + ); + } + + public function testBuild() + { + $storeId = 1; + $changedIds = []; + $valueFieldSuffix = '_value'; + $tableDropSuffix = ''; + $fillTmpTables = true; + $tableName = 'catalog_product_entity'; + $attributeTable = 'catalog_product_entity_int'; + $temporaryTableName = 'catalog_product_entity_int_tmp_indexer'; + $temporaryValueTableName = 'catalog_product_entity_int_tmp_indexer_value'; + $linkField = 'entity_id'; + $statusId = 22; + $eavCustomField = 'space_weight'; + $eavCustomValueField = $eavCustomField . $valueFieldSuffix; + $this->flatIndexerMock->expects($this->once())->method('getAttributes')->willReturn([]); + $this->flatIndexerMock->expects($this->exactly(3))->method('getFlatColumns') + ->willReturnOnConsecutiveCalls([], [$eavCustomValueField => []], [$eavCustomValueField => []]); + $this->flatIndexerMock->expects($this->once())->method('getFlatIndexes')->willReturn([]); + $statusAttributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $eavCustomAttributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $this->flatIndexerMock->expects($this->once())->method('getTablesStructure') + ->willReturn( + [ + 'catalog_product_entity' => [$linkField => $statusAttributeMock], + 'catalog_product_entity_int' => [ + $linkField => $statusAttributeMock, + $eavCustomField => $eavCustomAttributeMock + ] + ] + ); + $this->flatIndexerMock->expects($this->atLeastOnce())->method('getTable') + ->withConsecutive([$tableName], ['catalog_product_website']) + ->willReturnOnConsecutiveCalls($tableName, 'catalog_product_website'); + $this->flatIndexerMock->expects($this->once())->method('getAttribute') + ->with('status') + ->willReturn($statusAttributeMock); + $backendMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::class) + ->disableOriginalConstructor() + ->getMock(); + $backendMock->expects($this->atLeastOnce())->method('getTable')->willReturn($attributeTable); + $statusAttributeMock->expects($this->atLeastOnce())->method('getBackend')->willReturn( + $backendMock + ); + $eavCustomAttributeMock->expects($this->atLeastOnce())->method('getBackend')->willReturn( + $backendMock + ); + $statusAttributeMock->expects($this->atLeastOnce())->method('getId')->willReturn($statusId); + $tableMock = $this->getMockBuilder(\Magento\Framework\DB\Ddl\Table::class) + ->disableOriginalConstructor() + ->getMock(); + $this->connectionMock->expects($this->any())->method('newTable')->willReturn($tableMock); + $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $this->connectionMock->expects($this->atLeastOnce())->method('select')->willReturn($selectMock); + $selectMock->expects($this->once())->method('from')->with( + ['et' => 'catalog_product_entity_tmp_indexer'], + [$linkField, 'type_id', 'attribute_set_id'] + )->willReturnSelf(); + $selectMock->expects($this->atLeastOnce())->method('joinInner')->willReturnSelf(); + $selectMock->expects($this->exactly(3))->method('joinLeft') + ->withConsecutive( + [ + ['dstatus' => $attributeTable], + sprintf( + 'e.%s = dstatus.%s AND dstatus.store_id = %s AND dstatus.attribute_id = %s', + $linkField, + $linkField, + $storeId, + $statusId + ), + [] + ], + [ + $temporaryTableName, + "e.{$linkField} = {$temporaryTableName}.{$linkField}", + [$linkField, $eavCustomField] + ], + [ + $temporaryValueTableName, + "e.{$linkField} = {$temporaryValueTableName}.{$linkField}", + [$eavCustomValueField] + ] + )->willReturnSelf(); + $this->metadataPoolMock->expects($this->atLeastOnce())->method('getMetadata')->with(ProductInterface::class) + ->willReturn($this->metadataMock); + $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->storeManagerMock->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock); + $this->flatTableBuilder->build($storeId, $changedIds, $valueFieldSuffix, $tableDropSuffix, $fillTmpTables); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Plugin/IndexerConfigDataTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Plugin/IndexerConfigDataTest.php index 53d1a8fd004a0..097d70a5cf7cf 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Plugin/IndexerConfigDataTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Plugin/IndexerConfigDataTest.php @@ -1,6 +1,6 @@ connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $table = $this->getMockBuilder(\Magento\Framework\DB\Ddl\Table::class) + ->disableOriginalConstructor() + ->getMock(); + $table->expects($this->once())->method('addColumn') + ->with('test', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER) + ->willReturnSelf(); + $tableName = 'test_table'; + $this->connectionMock->expects($this->once()) + ->method('newTable') + ->with($tableName) + ->willReturn($table); + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + /** + * @var $builder \Magento\Catalog\Model\Indexer\Product\Flat\Table\Builder + */ + $builder = $objectManagerHelper->getObject( + \Magento\Catalog\Model\Indexer\Product\Flat\Table\Builder::class, + [ + 'connection' => $this->connectionMock, + 'tableName' => $tableName + ] + ); + $this->assertEquals($builder, $builder->addColumn('test', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/TableDataTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/TableDataTest.php index 0f47a675c8265..544bd865a5073 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/TableDataTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/TableDataTest.php @@ -1,6 +1,6 @@ getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); $localeFormatMock = $this->getMock(\Magento\Framework\Locale\FormatInterface::class, [], [], '', false); $groupManagement = $this->getMock(\Magento\Customer\Api\GroupManagementInterface::class, [], [], '', false); - $metadataPool = $this->getMock(\Magento\Framework\EntityManager\MetadataPool::class, [], [], '', false); + $scopeOverriddenValue = $this->getMock( + \Magento\Catalog\Model\Attribute\ScopeOverriddenValue::class, + [], + [], + '', + false + ); $this->_model = $this->getMockForAbstractClass( \Magento\Catalog\Model\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice::class, [ @@ -50,7 +56,7 @@ protected function setUp() 'localeFormat' => $localeFormatMock, 'catalogProductType' => $productTypeMock, 'groupManagement' => $groupManagement, - 'metadataPool' => $metadataPool + 'scopeOverriddenValue' => $scopeOverriddenValue ] ); $resource = $this->getMock(\StdClass::class, ['getMainTable']); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/Media/EntryConverterPoolTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/Media/EntryConverterPoolTest.php index 07b4b10834c71..827fe62fd6002 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/Media/EntryConverterPoolTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/Media/EntryConverterPoolTest.php @@ -1,6 +1,6 @@ getMockForAbstractClass( - \Magento\Framework\App\ScopeResolverInterface::class, - [], - '', - false - ); - $localeResolver = $this->getMockForAbstractClass( - \Magento\Framework\Locale\ResolverInterface::class, - [], - '', - false - ); - $currencyFactory = $this->getMock(\Magento\Directory\Model\CurrencyFactory::class, [], [], '', false); - $localeFormat = $objectHelper->getObject( - \Magento\Framework\Locale\Format::class, - [ - 'scopeResolver' => $scopeResolver, - 'localeResolver' => $localeResolver, - 'currencyFactory' => $currencyFactory, - ] - ); - - // the model we are testing + $localeFormat = $objectHelper->getObject(\Magento\Framework\Locale\Format::class); + $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->getMockForAbstractClass(); + $this->currencyFactory = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor()->getMock(); $this->model = $objectHelper->getObject( \Magento\Catalog\Model\Product\Attribute\Backend\Price::class, - ['localeFormat' => $localeFormat] - ); - - $attribute = $this->getMockForAbstractClass( - \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, - [], - '', - false + [ + 'localeFormat' => $localeFormat, + 'storeManager' => $this->storeManager, + 'currencyFactory' => $this->currencyFactory + ] ); - $this->model->setAttribute($attribute); + $this->attribute = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->setMethods(['getAttributeCode', 'isScopeWebsite']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->model->setAttribute($this->attribute); } /** @@ -111,4 +111,71 @@ public function dataProviderValidateForFailure() 'negative Lebanon' => ['-1 234'], ]; } + + public function testAfterSaveWithDifferentStores() + { + $newPrice = '9.99'; + $attributeCode = 'price'; + $defaultStoreId = 0; + $allStoreIds = [1, 2, 3]; + $object = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)->disableOriginalConstructor()->getMock(); + $object->expects($this->any())->method('getData')->with($attributeCode)->willReturn($newPrice); + $object->expects($this->any())->method('getOrigData')->with($attributeCode)->willReturn('7.77'); + $object->expects($this->any())->method('getStoreId')->willReturn($defaultStoreId); + $object->expects($this->never())->method('getStoreIds'); + $object->expects($this->never())->method('getWebsiteStoreIds'); + $this->attribute->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); + $this->attribute->expects($this->any())->method('isScopeWebsite') + ->willReturn(\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE); + $this->storeManager->expects($this->never())->method('getStore'); + + $object->expects($this->any())->method('addAttributeUpdate')->withConsecutive( + [ + $this->equalTo($attributeCode), + $this->equalTo($newPrice), + $this->equalTo($allStoreIds[0]) + ], + [ + $this->equalTo($attributeCode), + $this->equalTo($newPrice), + $this->equalTo($allStoreIds[1]) + ], + [ + $this->equalTo($attributeCode), + $this->equalTo($newPrice), + $this->equalTo($allStoreIds[2]) + ] + ); + $this->assertEquals($this->model, $this->model->afterSave($object)); + } + + public function testAfterSaveWithOldPrice() + { + $attributeCode = 'price'; + + $object = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)->disableOriginalConstructor()->getMock(); + $object->expects($this->any())->method('getData')->with($attributeCode)->willReturn('7.77'); + $object->expects($this->any())->method('getOrigData')->with($attributeCode)->willReturn('7.77'); + $this->attribute->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); + $this->attribute->expects($this->any())->method('getIsGlobal') + ->willReturn(\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE); + + $object->expects($this->never())->method('addAttributeUpdate'); + $this->assertEquals($this->model, $this->model->afterSave($object)); + } + + public function testAfterSaveWithGlobalPrice() + { + $attributeCode = 'price'; + + $object = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)->disableOriginalConstructor()->getMock(); + $object->expects($this->any())->method('getData')->with($attributeCode)->willReturn('9.99'); + $object->expects($this->any())->method('getOrigData')->with($attributeCode)->willReturn('7.77'); + $this->attribute->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); + $this->attribute->expects($this->any())->method('getIsGlobal') + ->willReturn(\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL); + + $object->expects($this->never())->method('addAttributeUpdate'); + $this->assertEquals($this->model, $this->model->afterSave($object)); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/StockTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/StockTest.php index ec9cb624db79a..74a42fa2cc845 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/StockTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/StockTest.php @@ -1,6 +1,6 @@ getStockData(); $this->assertEquals(0, $stockData['qty']); } + + public function testBeforeSaveNoStockData() + { + $object = new \Magento\Framework\DataObject( + [ + self::ATTRIBUTE_NAME => ['is_in_stock' => 1, 'qty' => 0] + ] + ); + + $this->model->beforeSave($object); + $this->assertNull($object->getStockData()); + $this->assertNull($object->getData(self::ATTRIBUTE_NAME)); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/WeightTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/WeightTest.php index 0ca0cdf7818b4..7d1bf213b7e34 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/WeightTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/WeightTest.php @@ -1,6 +1,6 @@ model->save($attributeMock); } + + public function testSaveSavesDefaultFrontendLabelIfItIsPresentInPayload() + { + $labelMock = $this->getMock(AttributeFrontendLabelInterface::class); + $labelMock->expects($this->any())->method('getStoreId')->willReturn(1); + $labelMock->expects($this->any())->method('getLabel')->willReturn('Store Scope Label'); + + $attributeId = 1; + $attributeCode = 'existing_attribute_code'; + $attributeMock = $this->getMock(Attribute::class, [], [], '', false); + $attributeMock->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); + $attributeMock->expects($this->any())->method('getAttributeId')->willReturn($attributeId); + $attributeMock->expects($this->any())->method('getDefaultFrontendLabel')->willReturn('Default Label'); + $attributeMock->expects($this->any())->method('getFrontendLabels')->willReturn([$labelMock]); + $attributeMock->expects($this->any())->method('getOptions')->willReturn([]); + + + $existingModelMock = $this->getMock(Attribute::class, [], [], '', false); + $existingModelMock->expects($this->any())->method('getAttributeId')->willReturn($attributeId); + $existingModelMock->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); + + $this->eavAttributeRepositoryMock->expects($this->any()) + ->method('get') + ->with(ProductAttributeInterface::ENTITY_TYPE_CODE, $attributeCode) + ->willReturn($existingModelMock); + + $attributeMock->expects($this->once()) + ->method('setDefaultFrontendLabel') + ->with( + [ + 0 => 'Default Label', + 1 => 'Store Scope Label' + ] + ); + $this->attributeResourceMock->expects($this->once())->method('save')->with($attributeMock); + + $this->model->save($attributeMock); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/SetManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/SetManagementTest.php index 25553562d2a0b..6d70c1611cd5c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/SetManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/SetManagementTest.php @@ -1,7 +1,7 @@ storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManagerInterface::class); $this->storeMock = $this->getMock(\Magento\Store\Model\Store::class, [], [], '', false); $this->cacheConfig = $this->getMock(\Magento\Framework\App\Cache\Type\Config::class, [], [], '', false); $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->countryOfManufacture = $this->objectManagerHelper->getObject( + \Magento\Catalog\Model\Product\Attribute\Source\Countryofmanufacture::class, + [ + 'storeManager' => $this->storeManagerMock, + 'configCacheType' => $this->cacheConfig, + ] + ); + + $this->serializerMock = $this->getMock(SerializerInterface::class, [], [], '', false); + $this->objectManagerHelper->setBackwardCompatibleProperty( + $this->countryOfManufacture, + 'serializer', + $this->serializerMock + ); } /** @@ -51,15 +75,10 @@ public function testGetAllOptions($cachedDataSrl, $cachedDataUnsrl) ->method('load') ->with($this->equalTo('COUNTRYOFMANUFACTURE_SELECT_STORE_store_code')) ->will($this->returnValue($cachedDataSrl)); - - $countryOfManufacture = $this->objectManagerHelper->getObject( - \Magento\Catalog\Model\Product\Attribute\Source\Countryofmanufacture::class, - [ - 'storeManager' => $this->storeManagerMock, - 'configCacheType' => $this->cacheConfig, - ] - ); - $this->assertEquals($cachedDataUnsrl, $countryOfManufacture->getAllOptions()); + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->willReturn($cachedDataUnsrl); + $this->assertEquals($cachedDataUnsrl, $this->countryOfManufacture->getAllOptions()); } /** @@ -71,7 +90,7 @@ public function testGetAllOptionsDataProvider() { return [ - ['cachedDataSrl' => 'a:1:{s:3:"key";s:4:"data";}', 'cachedDataUnsrl' => ['key' => 'data']] + ['cachedDataSrl' => json_encode(['key' => 'data']), 'cachedDataUnsrl' => ['key' => 'data']] ]; } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Source/InputtypeTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Source/InputtypeTest.php index 8362fc8a76180..2e9ea0f698514 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Source/InputtypeTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Source/InputtypeTest.php @@ -1,6 +1,6 @@ getMock(\Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface::class); $entryId = 42; + $entrySecondId = 43; $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) ->willReturn($this->productMock); $existingEntryMock = $this->getMock( \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface::class ); + $existingSecondEntryMock = $this->getMock( + \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface::class + ); + $existingEntryMock->expects($this->once())->method('getId')->willReturn($entryId); + $existingEntryMock->expects($this->once())->method('getTypes')->willReturn(['small_image']); + $existingEntryMock->expects($this->once())->method('setTypes')->with(['small_image']); + $existingSecondEntryMock->expects($this->once())->method('getId')->willReturn($entrySecondId); + $existingSecondEntryMock->expects($this->once())->method('getTypes')->willReturn(['image']); + $existingSecondEntryMock->expects($this->once())->method('setTypes')->with([]); $this->productMock->expects($this->once())->method('getMediaGalleryEntries') - ->willReturn([$existingEntryMock]); - $entryMock->expects($this->once())->method('getId')->willReturn($entryId); + ->willReturn([$existingEntryMock, $existingSecondEntryMock]); + + $entryMock->expects($this->exactly(2))->method('getId')->willReturn($entryId); $entryMock->expects($this->once())->method('getFile')->willReturn("base64"); $entryMock->expects($this->once())->method('setId')->with(null); + $entryMock->expects($this->exactly(2))->method('getTypes')->willReturn(['image']); $this->productMock->expects($this->once())->method('setMediaGalleryEntries') - ->willReturn([$entryMock]); + ->with([$entryMock, $existingSecondEntryMock]) + ->willReturnSelf(); $this->productRepositoryMock->expects($this->once())->method('save')->with($this->productMock); $this->assertTrue($this->model->update($productSku, $entryMock)); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/MimeTypeExtensionMapTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/MimeTypeExtensionMapTest.php index ebf351da74102..8ec55bcec098a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/MimeTypeExtensionMapTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/MimeTypeExtensionMapTest.php @@ -1,7 +1,7 @@ getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $productMock->expects($this->exactly($setDataExpectsCalls)) + ->method('setData') + ->with($setDataArgument, 'no_selection'); + + $this->mediaConfig->expects($this->once()) + ->method('getMediaAttributeCodes') + ->willReturn(['image', 'small_image']); + + $this->assertSame($this->model, $this->model->clearMediaAttribute($productMock, $mediaAttribute)); + } + + /** + * @return array + */ + public function clearMediaAttributeDataProvider() + { + return [ + [ + 'setDataExpectsCalls' => 1, + 'setDataArgument' => 'image', + 'mediaAttribute' => 'image', + ], + [ + 'setDataExpectsCalls' => 1, + 'setDataArgument' => 'image', + 'mediaAttribute' => ['image'], + ], + [ + 'setDataExpectsCalls' => 0, + 'setDataArgument' => null, + 'mediaAttribute' => 'some_image', + ], + [ + 'setDataExpectsCalls' => 0, + 'setDataArgument' => null, + 'mediaAttribute' => ['some_image'], + ], + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/CacheTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/CacheTest.php index de60264c33163..f12638ac40c8f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/CacheTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/CacheTest.php @@ -1,6 +1,6 @@ context = $this->getMock(\Magento\Framework\Model\Context::class, [], [], '', false); @@ -99,7 +119,6 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['create', 'isFile', 'isExist', 'getAbsolutePath']) ->getMock(); - $this->mediaDirectory->expects($this->once())->method('create')->will($this->returnValue(true)); $this->filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false); $this->filesystem->expects($this->once())->method('getDirectoryWrite') @@ -110,20 +129,49 @@ protected function setUp() $this->fileSystem = $this->getMock(\Magento\Framework\View\FileSystem::class, [], [], '', false); $this->scopeConfigInterface = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $context = $this->getMockBuilder(\Magento\Framework\Model\Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->image = new \Magento\Catalog\Model\Product\Image( + $context, + $this->registry, + $this->storeManager, + $this->config, + $this->coreFileHelper, + $this->filesystem, + $this->factory, + $this->repository, + $this->fileSystem, + $this->scopeConfigInterface + ); + //Settings for backward compatible property $objectManagerHelper = new ObjectManagerHelper($this); - $this->image = $objectManagerHelper->getObject( - \Magento\Catalog\Model\Product\Image::class, - [ - 'registry' => $this->registry, - 'storeManager' => $this->storeManager, - 'catalogProductMediaConfig' => $this->config, - 'coreFileStorageDatabase' => $this->coreFileHelper, - 'filesystem' => $this->filesystem, - 'imageFactory' => $this->factory, - 'assetRepo' => $this->repository, - 'viewFileSystem' => $this->fileSystem, - 'scopeConfig' => $this->scopeConfigInterface - ] + $this->imageAsset = $this->getMockBuilder(\Magento\Framework\View\Asset\LocalInterface::class) + ->getMockForAbstractClass(); + $objectManagerHelper->setBackwardCompatibleProperty( + $this->image, + 'imageAsset', + $this->imageAsset + ); + + $this->viewAssetImageFactory = $this->getMockBuilder(ImageFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $objectManagerHelper->setBackwardCompatibleProperty( + $this->image, + 'viewAssetImageFactory', + $this->viewAssetImageFactory + ); + + $this->viewAssetPlaceholderFactory = $this->getMockBuilder(PlaceholderFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $objectManagerHelper->setBackwardCompatibleProperty( + $this->image, + 'viewAssetPlaceholderFactory', + $this->viewAssetPlaceholderFactory ); } @@ -177,18 +225,39 @@ public function testSetGetBaseFile() $absolutePath = dirname(dirname(__DIR__)) . '/_files/catalog/product/somefile.png'; $this->mediaDirectory->expects($this->any())->method('getAbsolutePath') ->will($this->returnValue($absolutePath)); + $this->viewAssetImageFactory->expects($this->any()) + ->method('create') + ->with( + [ + 'miscParams' => [ + 'image_type' => null, + 'image_height' => null, + 'image_width' => null, + 'keep_aspect_ratio' => 'proportional', + 'keep_frame' => 'frame', + 'keep_transparency' => 'transparency', + 'constrain_only' => 'doconstrainonly', + 'background' => 'ffffff', + 'angle' => null, + 'quality' => 80, + ], + 'filePath' => '/somefile.png', + ] + ) + ->willReturn($this->imageAsset); + $this->viewAssetPlaceholderFactory->expects($this->never())->method('create'); + + $this->imageAsset->expects($this->any())->method('getSourceFile')->willReturn('catalog/product/somefile.png'); $this->image->setBaseFile('/somefile.png'); $this->assertEquals('catalog/product/somefile.png', $this->image->getBaseFile()); - $this->assertEquals( - 'catalog/product/cache/1//beff4985b56e3afdbeabfc89641a4582/somefile.png', - $this->image->getNewFile() - ); } public function testSetBaseNoSelectionFile() { - $this->image->setBaseFile('/no_selection'); - $this->assertTrue($this->image->getNewFile()); + $this->viewAssetPlaceholderFactory->expects($this->once())->method('create')->willReturn($this->imageAsset); + $this->imageAsset->expects($this->any())->method('getSourceFile')->willReturn('Default Placeholder Path'); + $this->image->setBaseFile('no_selection'); + $this->assertEquals('Default Placeholder Path', $this->image->getBaseFile()); } public function testSetGetImageProcessor() @@ -284,45 +353,45 @@ public function testSaveFile() )->disableOriginalConstructor()->getMock(); $this->image->setImageProcessor($imageProcessor); $this->coreFileHelper->expects($this->once())->method('saveFile')->will($this->returnValue(true)); - $absolutePath = dirname(dirname(__DIR__)) . '/_files/catalog/product/somefile.png'; - $this->mediaDirectory->expects($this->once())->method('getAbsolutePath') - ->will($this->returnValue($absolutePath)); $this->image->saveFile(); } public function testSaveFileNoSelection() { - $this->testSetBaseNoSelectionFile(); + $imageProcessor = $this->getMockBuilder( + \Magento\Framework\Image::class + )->disableOriginalConstructor()->getMock(); + $this->image->setImageProcessor($imageProcessor); $this->assertSame($this->image, $this->image->saveFile()); } public function testGetUrl() { $this->testSetGetBaseFile(); - $url = $this->image->getUrl(); - $this->assertEquals( - 'http://magento.com/media/catalog/product/cache/1//beff4985b56e3afdbeabfc89641a4582/somefile.png', - $url - ); + $this->imageAsset->expects($this->any())->method('getUrl')->will($this->returnValue('url of exist image')); + $this->assertEquals('url of exist image', $this->image->getUrl()); } public function testGetUrlNoSelection() { - $this->testSetBaseNoSelectionFile(); - $this->repository->expects($this->once())->method('getUrl')->will($this->returnValue('someurl')); - $this->assertEquals('someurl', $this->image->getUrl()); + $this->viewAssetPlaceholderFactory->expects($this->once())->method('create')->willReturn($this->imageAsset); + $this->imageAsset->expects($this->any())->method('getUrl')->will($this->returnValue('Default Placeholder URL')); + $this->image->setBaseFile('no_selection'); + $this->assertEquals('Default Placeholder URL', $this->image->getUrl()); } public function testSetGetDestinationSubdir() { - $this->image->setDestinationSubdir('somesubdir'); - $this->assertEquals('somesubdir', $this->image->getDestinationSubdir()); + $this->image->setDestinationSubdir('image_type'); + $this->assertEquals('image_type', $this->image->getDestinationSubdir()); } public function testIsCached() { $this->testSetGetBaseFile(); + $absolutePath = dirname(dirname(__DIR__)) . '/_files/catalog/product/watermark/somefile.png'; + $this->imageAsset->expects($this->any())->method('getPath')->willReturn($absolutePath); $this->assertTrue($this->image->isCached()); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Initialization/Helper/ProductLinksTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Initialization/Helper/ProductLinksTest.php index 34cb5c81a1291..681e662c69ccd 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Initialization/Helper/ProductLinksTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Initialization/Helper/ProductLinksTest.php @@ -1,6 +1,6 @@ getMock( - \Magento\Catalog\Model\ResourceModel\Product\Option\CollectionFactory::class, - ['create'], - [], - '', - false - ); + $this->optionCollectionFactory = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); $metadataPool = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) ->disableOriginalConstructor() ->getMock(); @@ -96,7 +100,7 @@ protected function setUp() $this->optionRepository, [ 'optionFactory' => $optionFactory, - 'optionCollectionFactory' => $optionCollectionFactory, + 'collectionFactory' => $this->optionCollectionFactory, 'metadataPool' => $metadataPool ] ); @@ -240,4 +244,75 @@ private function setProperties($object, $properties = []) } } } + + /** + * @expectedException \Magento\Framework\Exception\CouldNotSaveException + * @expectedExceptionMessage ProductSku should be specified + */ + public function testSaveCouldNotSaveException() + { + $this->optionMock->expects($this->once())->method('getProductSku')->willReturn(null); + $this->optionRepository->save($this->optionMock); + } + + /** + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSaveNoSuchEntityException() + { + $productSku = 'simple_product'; + $optionId = 1; + $productOptionId = 2; + $this->optionMock->expects($this->once())->method('getProductSku')->willReturn($productSku); + $this->productRepositoryMock + ->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $productOption = clone $this->optionMock; + $this->optionMock->expects($this->any())->method('getOptionId')->willReturn($optionId); + $productOption->expects($this->any())->method('getOptionId')->willReturn($productOptionId); + $this->productMock->expects($this->once())->method('getOptions')->willReturn([$productOption]); + $this->optionRepository->save($this->optionMock); + } + + public function testSave() + { + $productSku = 'simple_product'; + $optionId = 1; + $originalValue1 = $this->getMockBuilder(\Magento\Catalog\Model\Product\Option::class) + ->disableOriginalConstructor() + ->getMock(); + $originalValue2 = clone $originalValue1; + $originalValue3 = clone $originalValue1; + + $originalValue1->expects($this->at(0))->method('getData')->with('option_type_id')->willReturn(10); + $originalValue1->expects($this->once())->method('setData')->with('is_delete', 1); + $originalValue2->expects($this->once())->method('getData')->with('option_type_id')->willReturn(4); + $originalValue3->expects($this->once())->method('getData')->with('option_type_id')->willReturn(5); + + $this->optionMock->expects($this->once())->method('getProductSku')->willReturn($productSku); + $this->productRepositoryMock + ->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $this->optionMock->expects($this->any())->method('getOptionId')->willReturn($optionId); + $this->productMock->expects($this->once())->method('getOptions')->willReturn([]); + $this->optionMock->expects($this->once())->method('getData')->with('values')->willReturn([ + ['option_type_id' => 4], + ['option_type_id' => 5] + ]); + $optionCollection = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Option\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $optionCollection->expects($this->once())->method('getProductOptions')->willReturn([$this->optionMock]); + $this->optionCollectionFactory->expects($this->once())->method('create')->willReturn($optionCollection); + $this->optionMock->expects($this->once())->method('getValues')->willReturn([ + $originalValue1, + $originalValue2, + $originalValue3 + ]); + $this->assertEquals($this->optionMock, $this->optionRepository->save($this->optionMock)); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php new file mode 100644 index 0000000000000..cf29f44550f1f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php @@ -0,0 +1,72 @@ +entity = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $this->optionMock = $this->getMockBuilder(Option::class) + ->disableOriginalConstructor() + ->getMock(); + $this->optionRepository = $this->getMockBuilder(Repository::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new SaveHandler($this->optionRepository); + } + + public function testExecute() + { + $this->optionMock->expects($this->any())->method('getOptionId')->willReturn(5); + $this->entity->expects($this->once())->method('getOptions')->willReturn([$this->optionMock]); + + $secondOptionMock = $this->getMockBuilder(Option::class) + ->disableOriginalConstructor() + ->getMock(); + $secondOptionMock->expects($this->once())->method('getOptionId')->willReturn(6); + + $this->optionRepository + ->expects($this->once()) + ->method('getProductOptions') + ->with($this->entity) + ->willReturn([$this->optionMock, $secondOptionMock]); + + $this->optionRepository->expects($this->once())->method('delete'); + $this->optionRepository->expects($this->once())->method('save')->with($this->optionMock); + + $this->assertEquals($this->entity, $this->model->execute($this->entity)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FactoryTest.php index 1b66800ca8c01..d9b381ec3a365 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FactoryTest.php @@ -1,6 +1,6 @@ objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->rootDirectory = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\ReadInterface::class) + $this->filesystemMock = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor() - ->setMethods(['isFile', 'isReadable', 'getAbsolutePath']) - ->getMockForAbstractClass(); + ->getMock(); + + $this->rootDirectory = $this->getMockBuilder(ReadInterface::class) + ->getMock(); + + $this->filesystemMock->expects($this->any()) + ->method('getDirectoryRead') + ->with(DirectoryList::MEDIA, DriverPool::FILE) + ->willReturn($this->rootDirectory); + + $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->urlBuilder = $this->getMockBuilder(\Magento\Catalog\Model\Product\Option\UrlBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->escaper = $this->getMockBuilder(\Magento\Framework\Escaper::class) + ->disableOriginalConstructor() + ->getMock(); $this->coreFileStorageDatabase = $this->getMock( \Magento\MediaStorage\Helper\File\Storage\Database::class, @@ -48,24 +98,65 @@ protected function getFileObject() return $this->objectManager->getObject( \Magento\Catalog\Model\Product\Option\Type\File::class, [ - 'saleableItem' => $this->rootDirectory, - 'priceCurrency' => $this->coreFileStorageDatabase + 'filesystem' => $this->filesystemMock, + 'coreFileStorageDatabase' => $this->coreFileStorageDatabase, + 'serializer' => $this->serializer, + 'urlBuilder' => $this->urlBuilder, + 'escaper' => $this->escaper ] ); } + public function testGetCustomizedView() + { + $fileObject = $this->getFileObject(); + $optionInfo = ['option_value' => 'some serialized data']; + + $dataAfterSerialize = ['some' => 'array']; + + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with('some serialized data') + ->willReturn($dataAfterSerialize); + + $this->urlBuilder->expects($this->once()) + ->method('getUrl') + ->willReturn('someUrl'); + + $this->escaper->expects($this->once()) + ->method('escapeHtml') + ->willReturn('string'); + + $this->assertEquals( + 'string ', + $fileObject->getCustomizedView($optionInfo) + ); + } + public function testCopyQuoteToOrder() { - $optionMock = $this->getMockBuilder( - \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface::class - )->disableOriginalConstructor()->setMethods(['getValue'])->getMockForAbstractClass(); + $optionMock = $this->getMockBuilder(OptionInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getValue']) + ->getMockForAbstractClass(); $quotePath = '/quote/path/path/uploaded.file'; $orderPath = '/order/path/path/uploaded.file'; + $quoteValue = "{\"quote_path\":\"$quotePath\",\"order_path\":\"$orderPath\"}"; + + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($quoteValue) + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); + $optionMock->expects($this->any()) ->method('getValue') - ->will($this->returnValue(['quote_path' => $quotePath, 'order_path' => $orderPath])); + ->will($this->returnValue($quoteValue)); $this->rootDirectory->expects($this->any()) ->method('isFile') @@ -93,4 +184,55 @@ public function testCopyQuoteToOrder() $fileObject->copyQuoteToOrder() ); } + + public function testGetFormattedOptionValue() + { + $resultValue = ['result']; + $optionValue = json_encode($resultValue); + $urlParameter = 'parameter'; + + $fileObject = $this->getFileObject(); + $fileObject->setCustomOptionUrlParams($urlParameter); + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($optionValue) + ->willReturn($resultValue); + + $resultValue['url'] = [ + 'route' => 'sales/download/downloadCustomOption', + 'params' => $fileObject->getCustomOptionUrlParams() + ]; + + $this->serializer->expects($this->once()) + ->method('serialize') + ->with($resultValue) + ->willReturn(json_encode($resultValue)); + + $option = $this->getMockBuilder(\Magento\Quote\Model\Quote\Item\Option::class) + ->setMethods(['setValue']) + ->disableOriginalConstructor() + ->getMock(); + + $option->expects($this->once()) + ->method('setValue') + ->with(json_encode($resultValue)); + + $fileObject->setConfigurationItemOption($option); + + $fileObject->getFormattedOptionValue($optionValue); + } + + public function testPrepareOptionValueForRequest() + { + $optionValue = 'string'; + $resultValue = ['result']; + $fileObject = $this->getFileObject(); + + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($optionValue) + ->willReturn($resultValue); + + $this->assertEquals($resultValue, $fileObject->prepareOptionValueForRequest($optionValue)); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/UrlBuilderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/UrlBuilderTest.php index 9b0e1e1627454..0d543d16286d7 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/UrlBuilderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/UrlBuilderTest.php @@ -1,6 +1,6 @@ pricePersistenceFactory = $this->getMockBuilder( + \Magento\Catalog\Model\Product\Price\PricePersistenceFactory::class + ) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->pricePersistence = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\PricePersistence::class) + ->disableOriginalConstructor() + ->getMock(); + $this->basePriceInterfaceFactory = $this->getMockBuilder( + \Magento\Catalog\Api\Data\BasePriceInterfaceFactory::class + ) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->basePriceInterface = $this->getMockBuilder(\Magento\Catalog\Api\Data\BasePriceInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->storeRepository = $this->getMockBuilder(\Magento\Store\Api\StoreRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->invalidSkuProcessor = $this + ->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\InvalidSkuProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->validationResult = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) + ->disableOriginalConstructor() + ->getMock(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\BasePriceStorage::class, + [ + 'pricePersistenceFactory' => $this->pricePersistenceFactory, + 'basePriceInterfaceFactory' => $this->basePriceInterfaceFactory, + 'productIdLocator' => $this->productIdLocator, + 'storeRepository' => $this->storeRepository, + 'invalidSkuProcessor' => $this->invalidSkuProcessor, + 'validationResult' => $this->validationResult, + 'allowedProductTypes' => ['simple', 'virtual', 'bundle', 'downloadable'], + ] + ); + } + + /** + * Test get method. + * + * @return void + */ + public function testGet() + { + $skus = ['sku_1', 'sku_2', 'sku_3']; + $validSkus = ['sku_1', 'sku_2']; + $rawPrices = [ + [ + 'row_id' => 1, + 'value' => 15, + 'store_id' => 1 + ], + [ + 'row_id' => 2, + 'value' => 35, + 'store_id' => 1 + ] + ]; + $this->invalidSkuProcessor->expects($this->once()) + ->method('filterSkuList') + ->with($skus, ['simple', 'virtual', 'bundle', 'downloadable'], 1) + ->willReturn($validSkus); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'price']) + ->willReturn($this->pricePersistence); + $this->pricePersistence->expects($this->once())->method('get')->with($validSkus)->willReturn($rawPrices); + $this->basePriceInterfaceFactory + ->expects($this->atLeastOnce()) + ->method('create') + ->willReturn($this->basePriceInterface); + $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id'); + $this->pricePersistence + ->expects($this->atLeastOnce()) + ->method('retrieveSkuById') + ->willReturnOnConsecutiveCalls('sku_1', 'sku_2'); + $this->basePriceInterface + ->expects($this->atLeastOnce()) + ->method('setSku') + ->withConsecutive(['sku_1'], ['sku_2']) + ->willReturnSelf(); + $this->basePriceInterface + ->expects($this->atLeastOnce()) + ->method('setPrice') + ->withConsecutive([15], [35]) + ->willReturnSelf(); + $this->basePriceInterface + ->expects($this->atLeastOnce()) + ->method('setStoreId') + ->withConsecutive([1], [1]) + ->willReturnSelf(); + + $this->model->get($skus); + } + + /** + * Test update method. + * + * @return void + */ + public function testUpdate() + { + $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => + [ + 1 => [ + $this->basePriceInterface + ] + ] + ]; + $this->basePriceInterface->expects($this->atLeastOnce())->method('getSku')->willReturn($sku); + $this->invalidSkuProcessor->expects($this->once()) + ->method('retrieveInvalidSkuList') + ->with([1 => $sku], ['simple', 'virtual', 'bundle', 'downloadable'], 1) + ->willReturn([]); + $this->basePriceInterface->expects($this->atLeastOnce())->method('getPrice')->willReturn(15); + $this->basePriceInterface->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $this->validationResult->expects($this->once())->method('getFailedRowIds')->willReturn([]); + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with([$sku]) + ->willReturn($idsBySku); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'price']) + ->willReturn($this->pricePersistence); + $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id'); + $this->storeRepository->expects($this->once())->method('getById')->with(1)->willReturn($store); + $formattedPrices = [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15 + ] + ]; + $this->pricePersistence->expects($this->once())->method('update')->with($formattedPrices); + $this->validationResult->expects($this->any())->method('getFailedItems')->willReturn([]); + $this->assertEquals([], $this->model->update([1 => $this->basePriceInterface])); + } + + /** + * Test update method without SKU and with negative price. + * + * @return void + */ + public function testUpdateWithoutSkuAndWithNegativePrice() + { + $exception = new \Magento\Framework\Exception\NoSuchEntityException(); + $this->basePriceInterface->expects($this->atLeastOnce())->method('getSku')->willReturn(null); + $this->basePriceInterface->expects($this->atLeastOnce())->method('getPrice')->willReturn(-10); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'price']) + ->willReturn($this->pricePersistence); + $this->invalidSkuProcessor->expects($this->once()) + ->method('retrieveInvalidSkuList') + ->with([null], ['simple', 'virtual', 'bundle', 'downloadable'], 1) + ->willReturn([]); + $priceUpdateResult = $this->getMockBuilder(\Magento\Catalog\Api\Data\PriceUpdateResultInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->validationResult->expects($this->atLeastOnce()) + ->method('addFailedItem') + ->withConsecutive( + [ + 0, + __( + 'Invalid attribute %fieldName = %fieldValue.', + ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue'] + ), + ['fieldName' => 'SKU', 'fieldValue' => null] + ], + [ + 0, + __( + 'Invalid attribute %fieldName = %fieldValue.', + ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue'] + ), + ['fieldName' => 'Price', 'fieldValue' => -10] + ], + [ + 0, + __( + 'Requested store is not found. Row ID: SKU = %SKU, Store ID: %storeId.', + ['SKU' => null, 'storeId' => 10] + ), + ['SKU' => null, 'storeId' => 10] + ] + ); + $this->basePriceInterface->expects($this->atLeastOnce())->method('getStoreId')->willReturn(10); + $this->storeRepository->expects($this->once())->method('getById')->with(10)->willThrowException($exception); + $this->validationResult->expects($this->once())->method('getFailedRowIds')->willReturn([0 => 0]); + $this->pricePersistence->expects($this->once())->method('update')->with([]); + $this->validationResult->expects($this->once())->method('getFailedItems')->willReturn([$priceUpdateResult]); + + $this->assertEquals( + [$priceUpdateResult], + $this->model->update([$this->basePriceInterface]) + ); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php new file mode 100644 index 0000000000000..6efd2281b96a0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php @@ -0,0 +1,311 @@ +pricePersistenceFactory = $this->getMockBuilder( + \Magento\Catalog\Model\Product\Price\PricePersistenceFactory::class + ) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->pricePersistence = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\PricePersistence::class) + ->disableOriginalConstructor() + ->getMock(); + $this->costInterfaceFactory = $this->getMockBuilder(\Magento\Catalog\Api\Data\CostInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->costInterface = $this->getMockBuilder(\Magento\Catalog\Api\Data\CostInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->storeRepository = $this->getMockBuilder(\Magento\Store\Api\StoreRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->validationResult = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) + ->disableOriginalConstructor() + ->getMock(); + $this->invalidSkuProcessor = $this + ->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\InvalidSkuProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\CostStorage::class, + [ + 'pricePersistenceFactory' => $this->pricePersistenceFactory, + 'costInterfaceFactory' => $this->costInterfaceFactory, + 'productIdLocator' => $this->productIdLocator, + 'storeRepository' => $this->storeRepository, + 'validationResult' => $this->validationResult, + 'invalidSkuProcessor' => $this->invalidSkuProcessor, + 'allowedProductTypes' => ['simple', 'virtual', 'downloadable'], + ] + ); + } + + /** + * Test get method. + * + * @return void + */ + public function testGet() + { + $skus = ['sku_1', 'sku_2']; + $rawPrices = [ + [ + 'row_id' => 1, + 'value' => 15, + 'store_id' => 1 + ], + [ + 'row_id' => 2, + 'value' => 35, + 'store_id' => 1 + ] + ]; + $this->invalidSkuProcessor + ->expects($this->once()) + ->method('filterSkuList') + ->with($skus, ['simple', 'virtual', 'downloadable']) + ->willReturn($skus); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'cost']) + ->willReturn($this->pricePersistence); + $this->pricePersistence->expects($this->once())->method('get')->with($skus)->willReturn($rawPrices); + $this->costInterfaceFactory + ->expects($this->atLeastOnce()) + ->method('create') + ->willReturn($this->costInterface); + $this->pricePersistence + ->expects($this->atLeastOnce()) + ->method('retrieveSkuById') + ->willReturnOnConsecutiveCalls('sku_1', 'sku_2'); + $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id'); + $this->costInterface + ->expects($this->atLeastOnce()) + ->method('setSku') + ->withConsecutive(['sku_1'], ['sku_2']) + ->willReturnSelf(); + $this->costInterface + ->expects($this->atLeastOnce()) + ->method('setCost') + ->withConsecutive([15], [35]) + ->willReturnSelf(); + $this->costInterface + ->expects($this->atLeastOnce()) + ->method('setStoreId') + ->withConsecutive([1], [1]) + ->willReturnSelf(); + + $this->model->get($skus); + } + + /** + * Test update method. + * + * @return void + */ + public function testUpdate() + { + $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $this->costInterface->expects($this->atLeastOnce())->method('getSku')->willReturn($sku); + $this->invalidSkuProcessor + ->expects($this->once()) + ->method('retrieveInvalidSkuList') + ->willReturn([]); + $this->costInterface->expects($this->atLeastOnce())->method('getCost')->willReturn(15); + $this->costInterface->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $this->validationResult + ->expects($this->once()) + ->method('getFailedRowIds') + ->willReturn([]); + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with([$sku]) + ->willReturn($idsBySku); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'cost']) + ->willReturn($this->pricePersistence); + $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id'); + $this->storeRepository->expects($this->once())->method('getById')->with(1)->willReturn($store); + $formattedPrices = [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15 + ] + ]; + $this->pricePersistence->expects($this->once())->method('update')->with($formattedPrices); + $this->validationResult + ->expects($this->once()) + ->method('getFailedItems') + ->willReturn([]); + + $this->assertEmpty($this->model->update([$this->costInterface])); + } + + /** + * Test update method with negative cost and without SKU. + * + * @return void + */ + public function testUpdateWithNegativeCostAndWithoutSku() + { + $exception = new \Magento\Framework\Exception\NoSuchEntityException(); + $this->costInterface->expects($this->atLeastOnce())->method('getSku')->willReturn(null); + $this->costInterface->expects($this->atLeastOnce())->method('getCost')->willReturn(-15); + $this->costInterface->expects($this->atLeastOnce())->method('getStoreId')->willReturn(10); + $this->validationResult->expects($this->once())->method('getFailedRowIds')->willReturn([0 => 0]); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'cost']) + ->willReturn($this->pricePersistence); + $priceUpdateResult = $this->getMockBuilder(\Magento\Catalog\Api\Data\PriceUpdateResultInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->validationResult->expects($this->atLeastOnce()) + ->method('addFailedItem') + ->withConsecutive( + [ + 0, + __( + 'Invalid attribute %fieldName = %fieldValue.', + ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue'] + ), + ['fieldName' => 'SKU', 'fieldValue' => null] + ], + [ + 0, + __( + 'Invalid attribute Cost = %cost. Row ID: SKU = %SKU, Store ID: %storeId.', + ['cost' => -15, 'SKU' => null, 'storeId' => 10] + ), + ['cost' => -15, 'SKU' => null, 'storeId' => 10] + ], + [ + 0, + __( + 'Requested store is not found. Row ID: SKU = %SKU, Store ID: %storeId.', + ['SKU' => null, 'storeId' => 10] + ), + ['SKU' => null, 'storeId' => 10] + ] + ); + $this->storeRepository->expects($this->once())->method('getById')->with(10)->willThrowException($exception); + $this->invalidSkuProcessor + ->expects($this->once()) + ->method('retrieveInvalidSkuList') + ->willReturn([]); + $this->pricePersistence->expects($this->once())->method('update')->with([]); + $this->validationResult->expects($this->once())->method('getFailedItems')->willReturn([$priceUpdateResult]); + + $this->assertEquals( + [$priceUpdateResult], + $this->model->update([$this->costInterface]) + ); + } + + /** + * Test delete method. + * + * @return void + */ + public function testDelete() + { + $skus = ['sku_1', 'sku_2']; + $this->invalidSkuProcessor + ->expects($this->once()) + ->method('filterSkuList') + ->with($skus, ['simple', 'virtual', 'downloadable']) + ->willReturn($skus); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'cost']) + ->willReturn($this->pricePersistence); + $this->pricePersistence->expects($this->once())->method('delete')->with($skus); + + $this->model->delete($skus); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php new file mode 100644 index 0000000000000..3278bd9f51c10 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php @@ -0,0 +1,365 @@ +attributeResource = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Attribute::class) + ->disableOriginalConstructor()->getMock(); + $this->attributeRepository = $this->getMockBuilder( + \Magento\Catalog\Api\ProductAttributeRepositoryInterface::class + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->metadataPool = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) + ->disableOriginalConstructor() + ->setMethods(['getLinkField', 'getMetadata']) + ->getMock(); + $this->connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->productAttribute = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductAttributeInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\PricePersistence::class, + [ + 'attributeResource' => $this->attributeResource, + 'attributeRepository' => $this->attributeRepository, + 'productIdLocator' => $this->productIdLocator, + 'metadataPool' => $this->metadataPool, + ] + ); + } + + /** + * Test get method. + * + * @return void + */ + public function testGet() + { + $attributeId = 5; + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'sku_2' => + [ + 2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $select = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor()->getMock(); + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->attributeResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->connection); + $this->connection->expects($this->once())->method('select')->willReturn($select); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $select->expects($this->once())->method('from')->with('catalog_product_entity_decimal')->willReturnSelf(); + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $select + ->expects($this->atLeastOnce()) + ->method('where') + ->withConsecutive(['row_id IN (?)', [1, 2]], ['attribute_id = ?', $attributeId]) + ->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getMetadata')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id'); + $this->model->get($skus); + } + + /** + * Test update method. + * + * @return void + */ + public function testUpdate() + { + $attributeId = 5; + $prices = [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15 + ] + ]; + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->attributeResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->connection); + $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf(); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $this->connection + ->expects($this->once()) + ->method('insertOnDuplicate') + ->with( + 'catalog_product_entity_decimal', + [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15, + 'attribute_id' => 5, + ] + ], + ['value'] + ) + ->willReturnSelf(); + $this->connection->expects($this->once())->method('commit')->willReturnSelf(); + $this->model->update($prices); + } + + /** + * Test update method throws exception. + * + * @expectedException \Magento\Framework\Exception\CouldNotSaveException + * @expectedExceptionMessage Could not save Prices. + */ + public function testUpdateWithException() + { + $attributeId = 5; + $prices = [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15 + ] + ]; + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->attributeResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->connection); + $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf(); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $this->connection + ->expects($this->once()) + ->method('insertOnDuplicate') + ->with( + 'catalog_product_entity_decimal', + [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15, + 'attribute_id' => 5, + ] + ], + ['value'] + ) + ->willReturnSelf(); + $this->connection->expects($this->once())->method('commit')->willThrowException(new \Exception()); + $this->connection->expects($this->once())->method('rollback')->willReturnSelf(); + $this->model->update($prices); + } + + /** + * Test delete method. + * + * @return void + */ + public function testDelete() + { + $attributeId = 5; + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'sku_2' => + [ + 2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->attributeResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->connection); + $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf(); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $this->connection + ->expects($this->once()) + ->method('delete') + ->with( + 'catalog_product_entity_decimal', + [ + 'attribute_id = ?' => $attributeId, + 'row_id IN (?)' => [1, 2] + ] + ) + ->willReturnSelf(); + $this->connection->expects($this->once())->method('commit')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getMetadata')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id'); + $this->model->delete($skus); + } + + /** + * Test delete method throws exception. + * + * @expectedException \Magento\Framework\Exception\CouldNotDeleteException + * @expectedExceptionMessage Could not delete Prices + */ + public function testDeleteWithException() + { + $attributeId = 5; + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'sku_2' => + [ + 2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->attributeResource->expects($this->atLeastOnce(2))->method('getConnection') + ->willReturn($this->connection); + $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf(); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $this->connection + ->expects($this->once()) + ->method('delete') + ->with( + 'catalog_product_entity_decimal', + [ + 'attribute_id = ?' => $attributeId, + 'row_id IN (?)' => [1, 2] + ] + ) + ->willReturnSelf(); + $this->connection->expects($this->once())->method('commit')->willThrowException(new \Exception()); + $this->connection->expects($this->once())->method('rollBack')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getMetadata')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id'); + $this->model->delete($skus); + } + + /** + * Test retrieveSkuById method. + * + * @param int|null $expectedResult + * @param int $id + * @param array $skus + * @dataProvider dataProviderRetrieveSkuById + */ + public function testRetrieveSkuById($expectedResult, $id, array $skus) + { + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus') + ->willReturn($skus); + + $this->assertEquals($expectedResult, $this->model->retrieveSkuById($id, $skus)); + } + + /** + * Data provider for retrieveSkuById method. + * + * @return array + */ + public function dataProviderRetrieveSkuById() + { + return [ + [ + null, + 2, + ['sku_1' => [1 => 1]] + ], + [ + 'sku_1', + 1, + ['sku_1' => [1 => 1]] + ], + [ + null, + 1, + ['sku_1' => [2 => 1]] + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php new file mode 100644 index 0000000000000..d71856be51eba --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php @@ -0,0 +1,385 @@ +specialPriceResource = $this->getMockBuilder(\Magento\Catalog\Api\SpecialPriceInterface::class) + ->disableOriginalConstructor()->setMethods(['get', 'update', 'delete', 'getEntityLinkField'])->getMock(); + $this->productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class) + ->disableOriginalConstructor()->getMock(); + $this->storeRepository = $this->getMockBuilder(\Magento\Store\Api\StoreRepositoryInterface::class) + ->disableOriginalConstructor()->getMock(); + $this->invalidSkuProcessor = $this + ->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\InvalidSkuProcessor::class) + ->disableOriginalConstructor()->getMock(); + $this->validationResult = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) + ->disableOriginalConstructor()->getMock(); + $this->specialPriceFactory = $this->getMockBuilder( + \Magento\Catalog\Api\Data\SpecialPriceInterfaceFactory::class + )->disableOriginalConstructor()->setMethods(['create'])->getMock(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\SpecialPriceStorage::class, + [ + 'specialPriceResource' => $this->specialPriceResource, + 'specialPriceFactory' => $this->specialPriceFactory, + 'productIdLocator' => $this->productIdLocator, + 'storeRepository' => $this->storeRepository, + 'invalidSkuProcessor' => $this->invalidSkuProcessor, + 'validationResult' => $this->validationResult, + ] + ); + } + + /** + * Test get method. + * + * @return void + */ + public function testGet() + { + $skus = ['sku_1', 'sku_2']; + $rawPrices = [ + [ + 'entity_id' => 1, + 'value' => 15, + 'store_id' => 1, + 'sku' => 'sku_1', + 'price_from' => '2016-12-20 01:02:03', + 'price_to' => '2016-12-21 01:02:03', + ], + [ + 'entity_id' => 2, + 'value' => 15, + 'store_id' => 1, + 'price_from' => '2016-12-20 01:02:03', + 'price_to' => '2016-12-21 01:02:03', + ], + [ + 'entity_id' => 3, + 'value' => 15, + 'store_id' => 1, + 'price_from' => '2016-12-20 01:02:03', + 'price_to' => '2016-12-21 01:02:03', + ], + ]; + $this->invalidSkuProcessor->expects($this->once())->method('filterSkuList')->with($skus, [])->willReturn($skus); + $this->specialPriceResource->expects($this->once())->method('get')->willReturn($rawPrices); + $this->specialPriceResource->expects($this->atLeastOnce()) + ->method('getEntityLinkField')->willReturn('entity_id'); + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class) + ->disableOriginalConstructor()->getMock(); + $price->expects($this->exactly(3))->method('setPrice'); + $this->specialPriceFactory->expects($this->atLeastOnce())->method('create')->willReturn($price); + $this->productIdLocator->expects($this->atLeastOnce())->method('retrieveProductIdsBySkus')->willReturn( + [ + 'sku_2' => [2 => 'prod'] + ] + ); + $this->model->get($skus); + } + + /** + * Test update method. + * + * @return void + */ + public function testUpdate() + { + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class) + ->disableOriginalConstructor()->getMock(); + $prices = [1 => $price]; + $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1'); + $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15); + $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-20 01:02:03'); + $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03'); + $this->invalidSkuProcessor->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]); + $this->storeRepository->expects($this->once())->method('getById'); + $this->validationResult->expects($this->never())->method('addFailedItem'); + $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]); + $this->specialPriceResource->expects($this->once())->method('update')->with($prices); + + $this->model->update($prices); + } + + /** + * Test update method with invalid sku. + * + * @return void + */ + public function testUpdateWithInvalidSku() + { + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class) + ->disableOriginalConstructor()->getMock(); + $prices = [1 => $price]; + $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1'); + $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15); + $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-20 01:02:03'); + $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03'); + $this->invalidSkuProcessor->expects($this->once())->method('retrieveInvalidSkuList')->willReturn(['sku_1']); + $this->storeRepository->expects($this->once())->method('getById'); + $this->validationResult + ->expects($this->once()) + ->method('addFailedItem') + ->with( + 1, + __( + 'Requested product doesn\'t exist. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => '2016-12-20 01:02:03', + 'priceTo' => '2016-12-21 01:02:03' + ] + ), + [ + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => '2016-12-20 01:02:03', + 'priceTo' => '2016-12-21 01:02:03' + ] + ); + $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]); + $this->specialPriceResource->expects($this->once())->method('update')->with([]); + + $this->model->update($prices); + } + + /** + * Test update method with price = null. + * + * @return void + */ + public function testUpdateWithoutPrice() + { + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class) + ->disableOriginalConstructor()->getMock(); + $prices = [1 => $price]; + $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1'); + $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(null); + $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-20 01:02:03'); + $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03'); + $this->invalidSkuProcessor->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]); + $this->storeRepository->expects($this->once())->method('getById'); + $this->validationResult->expects($this->once()) + ->method('addFailedItem') + ->with( + 1, + __( + 'Invalid attribute Price = %price. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'price' => null, + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => '2016-12-20 01:02:03', + 'priceTo' => '2016-12-21 01:02:03' + ] + ), + [ + 'price' => null, + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => '2016-12-20 01:02:03', + 'priceTo' => '2016-12-21 01:02:03' + ] + ); + $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]); + $this->specialPriceResource->expects($this->once())->method('update')->with([]); + + $this->model->update($prices); + } + + /** + * Test update method with price = null. + * + * @return void + */ + public function testUpdateWithException() + { + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class) + ->disableOriginalConstructor()->getMock(); + $prices = [1 => $price]; + $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1'); + $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15); + $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-20 01:02:03'); + $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03'); + $this->invalidSkuProcessor->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]); + $this->storeRepository->expects($this->once())->method('getById') + ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); + $this->validationResult->expects($this->once()) + ->method('addFailedItem') + ->with( + 1, + __( + 'Requested store is not found. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => '2016-12-20 01:02:03', + 'priceTo' => '2016-12-21 01:02:03' + ] + ), + [ + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => '2016-12-20 01:02:03', + 'priceTo' => '2016-12-21 01:02:03' + ] + ); + $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]); + $this->specialPriceResource->expects($this->once())->method('update')->with([]); + + $this->model->update($prices); + } + + /** + * Test update method with incorrect price_from field. + * + * @return void + */ + public function testUpdateWithIncorrectPriceFrom() + { + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class) + ->disableOriginalConstructor()->getMock(); + $prices = [1 => $price]; + $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1'); + $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15); + $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('incorrect'); + $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03'); + $this->invalidSkuProcessor->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]); + $this->storeRepository->expects($this->once())->method('getById'); + $this->validationResult->expects($this->once()) + ->method('addFailedItem') + ->with( + 1, + __( + 'Invalid attribute %label = %priceTo. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'label' => 'Price From', + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => 'incorrect', + 'priceTo' => '2016-12-21 01:02:03' + ] + ), + [ + 'label' => 'Price From', + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => 'incorrect', + 'priceTo' => '2016-12-21 01:02:03' + ] + ); + $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]); + $this->specialPriceResource->expects($this->once())->method('update')->with([]); + + $this->model->update($prices); + } + + /** + * Test update method with incorrect price_to field. + * + * @return void + */ + public function testUpdateWithIncorrectPriceTo() + { + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class) + ->disableOriginalConstructor()->getMock(); + $prices = [1 => $price]; + $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1'); + $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15); + $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1); + $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-21 01:02:03'); + $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('incorrect'); + $this->invalidSkuProcessor->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]); + $this->storeRepository->expects($this->once())->method('getById'); + $this->validationResult->expects($this->once()) + ->method('addFailedItem') + ->with( + 1, + __( + 'Invalid attribute %label = %priceTo. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'label' => 'Price To', + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => '2016-12-21 01:02:03', + 'priceTo' => 'incorrect' + ] + ), + [ + 'label' => 'Price To', + 'SKU' => 'sku_1', + 'storeId' => 1, + 'priceFrom' => '2016-12-21 01:02:03', + 'priceTo' => 'incorrect' + ] + ); + $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]); + $this->specialPriceResource->expects($this->once())->method('update')->with([]); + + $this->model->update($prices); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php new file mode 100644 index 0000000000000..7c90444f90b58 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php @@ -0,0 +1,300 @@ +tierPricePersistence = $this->getMockBuilder( + \Magento\Catalog\Model\Product\Price\TierPricePersistence::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->tierPricePersistence->expects($this->any()) + ->method('getEntityLinkField') + ->willReturn('row_id'); + $this->tierPriceValidator = $this->getMockBuilder( + \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->tierPriceFactory = $this->getMockBuilder( + \Magento\Catalog\Model\Product\Price\TierPriceFactory::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->priceIndexer = $this->getMockBuilder(\Magento\Catalog\Model\Indexer\Product\Price::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->config = $this->getMockBuilder(\Magento\PageCache\Model\Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->typeList = $this->getMockBuilder(\Magento\Framework\App\Cache\TypeListInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->tierPriceStorage = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\TierPriceStorage::class, + [ + 'tierPricePersistence' => $this->tierPricePersistence, + 'tierPriceValidator' => $this->tierPriceValidator, + 'tierPriceFactory' => $this->tierPriceFactory, + 'priceIndexer' => $this->priceIndexer, + 'productIdLocator' => $this->productIdLocator, + 'config' => $this->config, + 'typeList' => $this->typeList, + ] + ); + } + + /** + * Test get method. + * + * @return void + */ + public function testGet() + { + $skus = ['simple', 'virtual']; + $this->tierPriceValidator + ->expects($this->once()) + ->method('validateSkus') + ->with($skus) + ->willReturn($skus); + $this->productIdLocator->expects($this->atLeastOnce()) + ->method('retrieveProductIdsBySkus') + ->with(['simple', 'virtual']) + ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]); + $this->tierPricePersistence->expects($this->once()) + ->method('get') + ->willReturn( + [ + [ + 'value_id' => 1, + 'row_id' => 2, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 2.0000, + 'value' => 2.0000, + 'percentage_value' => null, + 'website_id' => 0 + ], + [ + 'value_id' => 2, + 'row_id' => 3, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 3.0000, + 'value' => 3.0000, + 'percentage_value' => null, + 'website_id' => 0 + ] + ] + ); + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); + $this->tierPriceFactory->expects($this->atLeastOnce())->method('create')->willReturn($price); + $prices = $this->tierPriceStorage->get($skus); + $this->assertNotEmpty($prices); + $this->assertEquals(2, count($prices)); + } + + /** + * Test update method. + * + * @return void + */ + public function testUpdate() + { + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); + $result = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) + ->disableOriginalConstructor() + ->getMock(); + $result->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]); + $this->productIdLocator->expects($this->atLeastOnce()) + ->method('retrieveProductIdsBySkus') + ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]); + $this->tierPriceValidator + ->expects($this->atLeastOnce()) + ->method('retrieveValidationResult') + ->willReturn($result); + $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn( + [ + 'row_id' => 2, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 2, + 'value' => 3, + 'percentage_value' => null, + 'website_id' => 0 + ] + ); + $this->tierPricePersistence->expects($this->once()) + ->method('get') + ->willReturn( + [ + [ + 'value_id' => 1, + 'row_id' => 2, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 2.0000, + 'value' => 2.0000, + 'percentage_value' => null, + 'website_id' => 0 + ] + ] + ); + $this->tierPricePersistence->expects($this->atLeastOnce())->method('update'); + $this->priceIndexer->expects($this->atLeastOnce())->method('execute'); + $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true); + $this->typeList->expects($this->atLeastOnce())->method('invalidate'); + $price->expects($this->atLeastOnce())->method('getSku')->willReturn('simple'); + $this->assertEmpty($this->tierPriceStorage->update([$price])); + } + + /** + * Test replace method. + * + * @return void + */ + public function testReplace() + { + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); + $price->expects($this->atLeastOnce())->method('getSku')->willReturn('virtual'); + $result = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) + ->disableOriginalConstructor() + ->getMock(); + $result->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]); + $this->productIdLocator->expects($this->atLeastOnce()) + ->method('retrieveProductIdsBySkus') + ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]); + + $this->tierPriceValidator + ->expects($this->atLeastOnce()) + ->method('retrieveValidationResult') + ->willReturn($result); + $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn( + [ + 'row_id' => 3, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 3, + 'value' => 7, + 'percentage_value' => null, + 'website_id' => 0 + ] + ); + $this->tierPricePersistence->expects($this->atLeastOnce())->method('replace'); + $this->priceIndexer->expects($this->atLeastOnce())->method('execute'); + $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true); + $this->typeList->expects($this->atLeastOnce())->method('invalidate'); + $this->assertEmpty($this->tierPriceStorage->replace([$price])); + } + + /** + * Test delete method. + * + * @return void + */ + public function testDelete() + { + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); + $price->expects($this->atLeastOnce())->method('getSku')->willReturn('simple'); + $result = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) + ->disableOriginalConstructor() + ->getMock(); + $result->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]); + $this->tierPriceValidator->expects($this->atLeastOnce()) + ->method('retrieveValidationResult') + ->willReturn($result); + $this->productIdLocator->expects($this->atLeastOnce()) + ->method('retrieveProductIdsBySkus') + ->willReturn(['simple' => ['2' => 'simple']]); + $this->tierPricePersistence->expects($this->once()) + ->method('get') + ->willReturn( + [ + [ + 'value_id' => 7, + 'row_id' => 7, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 5.0000, + 'value' => 6.0000, + 'percentage_value' => null, + 'website_id' => 0 + ] + ] + ); + $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn( + [ + 'row_id' => 3, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 3, + 'value' => 7, + 'percentage_value' => null, + 'website_id' => 0 + ] + ); + $this->tierPricePersistence->expects($this->atLeastOnce())->method('delete'); + $this->priceIndexer->expects($this->atLeastOnce())->method('execute'); + $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true); + $this->typeList->expects($this->atLeastOnce())->method('invalidate'); + $this->assertEmpty($this->tierPriceStorage->delete([$price])); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/InvalidSkuProcessorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/InvalidSkuProcessorTest.php new file mode 100644 index 0000000000000..3a79f7a48944e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/InvalidSkuProcessorTest.php @@ -0,0 +1,89 @@ +productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->productRepository = $this->getMockBuilder(\Magento\Catalog\Api\ProductRepositoryInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->invalidSkuProcessor = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\Validation\InvalidSkuProcessor::class, + [ + 'productIdLocator' => $this->productIdLocator, + 'productRepository' => $this->productRepository + ] + ); + } + + /** + * Prepare retrieveInvalidSkuList(). + * + * @param string $productType + * @param string $productSku + * @return void + */ + private function prepareRetrieveInvalidSkuListMethod($productType, $productSku) + { + $idsBySku = [$productSku => [235235235 => $productType]]; + $this->productIdLocator->expects($this->atLeastOnce())->method('retrieveProductIdsBySkus') + ->willReturn($idsBySku); + $product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) + ->setMethods(['getPriceType']) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $productPriceType = 0; + $product->expects($this->atLeastOnce())->method('getPriceType')->willReturn($productPriceType); + $this->productRepository->expects($this->atLeastOnce())->method('get')->willReturn($product); + } + + /** + * Test for retrieveInvalidSkuList(). + * + * @return void + */ + public function testRetrieveInvalidSkuList() + { + $productSku = 'LKJKJ2233636'; + $productType = \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE; + $methodParamSku = 'SDFSDF3242355'; + $skus = [$methodParamSku]; + $allowedProductTypes = [$productType]; + $allowedPriceTypeValue = true; + $this->prepareRetrieveInvalidSkuListMethod($productType, $productSku); + + $this->assertEquals( + [$methodParamSku, $productSku], + $this->invalidSkuProcessor->retrieveInvalidSkuList($skus, $allowedProductTypes, $allowedPriceTypeValue) + ); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php new file mode 100644 index 0000000000000..9274feed348b6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php @@ -0,0 +1,260 @@ +productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->searchCriteriaBuilder = $this->getMockBuilder(\Magento\Framework\Api\SearchCriteriaBuilder::class) + ->disableOriginalConstructor()->getMock(); + $this->filterBuilder = $this->getMockBuilder(\Magento\Framework\Api\FilterBuilder::class) + ->disableOriginalConstructor()->getMock(); + $this->customerGroupRepository = $this->getMockBuilder(\Magento\Customer\Api\GroupRepositoryInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->websiteRepository = $this->getMockBuilder(\Magento\Store\Api\WebsiteRepositoryInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->validationResult = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) + ->disableOriginalConstructor()->getMock(); + $this->invalidSkuProcessor = $this + ->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\InvalidSkuProcessor::class) + ->disableOriginalConstructor()->getMock(); + $this->tierPrice = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->tierPriceValidator = $objectManagerHelper->getObject( + \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator::class, + [ + 'productIdLocator' => $this->productIdLocator, + 'searchCriteriaBuilder' => $this->searchCriteriaBuilder, + 'filterBuilder' => $this->filterBuilder, + 'customerGroupRepository' => $this->customerGroupRepository, + 'websiteRepository' => $this->websiteRepository, + 'validationResult' => $this->validationResult, + 'invalidSkuProcessor' => $this->invalidSkuProcessor + ] + ); + } + + /** + * Prepare CustomerGroupRepository mock. + * + * @param array $returned + * @return void + */ + private function prepareCustomerGroupRepositoryMock(array $returned) + { + $searchCriteria = $this + ->getMockBuilder(\Magento\Framework\Api\Search\SearchCriteriaInterface::class) + ->disableOriginalConstructor()->getMock(); + $filter = $this->getMockBuilder(\Magento\Framework\Api\AbstractSimpleObject::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->filterBuilder->expects($this->atLeastOnce())->method('setField')->willReturnSelf(); + $this->filterBuilder->expects($this->atLeastOnce())->method('setValue')->willReturnSelf(); + $this->filterBuilder->expects($this->atLeastOnce())->method('create')->willReturn($filter); + $this->searchCriteriaBuilder->expects($this->atLeastOnce())->method('addFilters')->willReturnSelf(); + $this->searchCriteriaBuilder->expects($this->atLeastOnce())->method('create')->willReturn($searchCriteria); + $customerGroupSearchResults = $this + ->getMockBuilder(\Magento\Customer\Api\Data\GroupSearchResultsInterface::class) + ->disableOriginalConstructor()->getMock(); + $customerGroupSearchResults->expects($this->once())->method('getItems') + ->willReturn($returned['customerGroupSearchResults_getItems']); + $this->customerGroupRepository->expects($this->atLeastOnce())->method('getList') + ->willReturn($customerGroupSearchResults); + } + + /** + * Prepare retrieveValidationResult(). + * + * @param string $sku + * @param array $returned + * @return void + */ + private function prepareRetrieveValidationResultMethod($sku, array $returned) + { + $this->tierPrice->expects($this->atLeastOnce())->method('getSku')->willReturn($sku); + $tierPriceValue = 104; + $this->tierPrice->expects($this->atLeastOnce())->method('getPrice')->willReturn($tierPriceValue); + $this->tierPrice->expects($this->atLeastOnce())->method('getPriceType') + ->willReturn($returned['tierPrice_getPriceType']); + $qty = 0; + $this->tierPrice->expects($this->atLeastOnce())->method('getQuantity')->willReturn($qty); + $websiteId = 0; + $invalidWebsiteId = 4; + $this->tierPrice->expects($this->atLeastOnce())->method('getWebsiteId') + ->willReturnOnConsecutiveCalls($websiteId, $websiteId, $websiteId, $invalidWebsiteId, $websiteId); + $this->tierPrice->expects($this->atLeastOnce())->method('getCustomerGroup') + ->willReturn($returned['tierPrice_getCustomerGroup']); + $skuDiff = [$sku]; + $this->invalidSkuProcessor->expects($this->atLeastOnce())->method('retrieveInvalidSkuList') + ->willReturn($skuDiff); + $productId = 3346346; + $productType = \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE; + $idsBySku = [ + $sku => [$productId => $productType] + ]; + $this->productIdLocator->expects($this->atLeastOnce())->method('retrieveProductIdsBySkus') + ->willReturn($idsBySku); + } + + /** + * Test for validateSkus(). + * + * @return void + */ + public function testValidateSkus() + { + $skus = ['SDFS234234']; + $this->invalidSkuProcessor->expects($this->atLeastOnce()) + ->method('filterSkuList') + ->with($skus, []) + ->willReturn($skus); + + $this->assertEquals($skus, $this->tierPriceValidator->validateSkus($skus)); + } + + /** + * Test for retrieveValidationResult(). + * + * @param array $returned + * @dataProvider retrieveValidationResultDataProvider + * @return void + */ + public function testRetrieveValidationResult(array $returned) + { + $sku = 'ASDF234234'; + $prices = [$this->tierPrice]; + $existingPrices = [$this->tierPrice]; + $this->prepareRetrieveValidationResultMethod($sku, $returned); + $website = $this->getMockBuilder(\Magento\Store\Api\Data\WebsiteInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->websiteRepository->expects($this->atLeastOnce())->method('getById')->willReturn($website); + $this->prepareCustomerGroupRepositoryMock($returned); + + $this->assertEquals( + $this->validationResult, + $this->tierPriceValidator->retrieveValidationResult($prices, $existingPrices) + ); + } + + /** + * Data provider for retrieveValidationResult() test. + * + * @return array + */ + public function retrieveValidationResultDataProvider() + { + $customerGroupName = 'test_Group'; + $customerGroup = $this->getMockBuilder(\Magento\Customer\Api\Data\GroupInterface::class) + ->setMethods(['getCode', 'getId']) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $customerGroup->expects($this->atLeastOnce())->method('getCode')->willReturn($customerGroupName); + $customerGroupId = 23; + $customerGroup->expects($this->atLeastOnce())->method('getId')->willReturn($customerGroupId); + + return [ + [ + [ + 'tierPrice_getCustomerGroup' => $customerGroupName, + 'tierPrice_getPriceType' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT, + 'customerGroupSearchResults_getItems' => [$customerGroup] + ] + ], + [ + [ + 'tierPrice_getCustomerGroup' => $customerGroupName, + 'tierPrice_getPriceType' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED, + 'customerGroupSearchResults_getItems' => [] + ] + ] + ]; + } + + /** + * Test for retrieveValidationResult() with Exception. + * + * @return void + */ + public function testRetrieveValidationResultWithException() + { + $sku = 'ASDF234234'; + $customerGroupName = 'test_Group'; + $prices = [$this->tierPrice]; + $existingPrices = [$this->tierPrice]; + $returned = [ + 'tierPrice_getPriceType' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT, + 'customerGroupSearchResults_getItems' => [], + 'tierPrice_getCustomerGroup' => $customerGroupName, + ]; + $this->prepareRetrieveValidationResultMethod($sku, $returned); + $exception = new \Magento\Framework\Exception\NoSuchEntityException(); + $this->websiteRepository->expects($this->atLeastOnce())->method('getById')->willThrowException($exception); + $this->prepareCustomerGroupRepositoryMock($returned); + + $this->assertEquals( + $this->validationResult, + $this->tierPriceValidator->retrieveValidationResult($prices, $existingPrices) + ); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifier/CompositeTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifier/CompositeTest.php index 35aad84447526..7b27d3b07593f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifier/CompositeTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifier/CompositeTest.php @@ -1,6 +1,6 @@ product = $this->getMock( + \Magento\Catalog\Model\Product::class, + ['__wakeup', 'getCanShowPrice'], + [], + '', + false + ); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->object = $objectManager->getObject( + \Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver::class + ); + } + + public function testSalableItem() + { + $this->product->expects($this->any()) + ->method('getCanShowPrice') + ->willReturn(true); + + $result = $this->object->isSalable($this->product); + $this->assertTrue($result); + } + + public function testNotSalableItem() + { + $this->product->expects($this->any()) + ->method('getCanShowPrice') + ->willReturn(false); + + $result = $this->object->isSalable($this->product); + $this->assertFalse($result); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductList/ToolbarTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductList/ToolbarTest.php index 7bafd7a5b5686..a7eb5069d02f0 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductList/ToolbarTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductList/ToolbarTest.php @@ -1,6 +1,6 @@ metadataPool = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) + ->setMethods(['getMetadata']) + ->disableOriginalConstructor()->getMock(); + $this->collectionFactory = $this + ->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor()->getMock(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\ProductIdLocator::class, + [ + 'metadataPool' => $this->metadataPool, + 'collectionFactory' => $this->collectionFactory, + ] + ); + } + + /** + * Test retrieve + */ + public function testRetrieveProductIdsBySkus() + { + $skus = ['sku_1', 'sku_2']; + $collection = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Collection::class) + ->setMethods(['getIterator', 'addFieldToFilter']) + ->disableOriginalConstructor()->getMock(); + $product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) + ->setMethods(['getSku', 'getData', 'getTypeId']) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $metaDataInterface = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) + ->setMethods(['getLinkField']) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->collectionFactory->expects($this->once())->method('create')->willReturn($collection); + $collection->expects($this->once())->method('addFieldToFilter') + ->with(\Magento\Catalog\Api\Data\ProductInterface::SKU, ['in' => $skus])->willReturnSelf(); + $collection->expects($this->once())->method('getIterator')->willReturn(new \ArrayIterator([$product])); + $this->metadataPool + ->expects($this->once()) + ->method('getMetadata') + ->with(\Magento\Catalog\Api\Data\ProductInterface::class) + ->willReturn($metaDataInterface); + $metaDataInterface->expects($this->once())->method('getLinkField')->willReturn('entity_id'); + $product->expects($this->once())->method('getSku')->willReturn('sku_1'); + $product->expects($this->once())->method('getData')->with('entity_id')->willReturn(1); + $product->expects($this->once())->method('getTypeId')->willReturn('simple'); + $this->assertEquals( + ['sku_1' => [1 => 'simple']], + $this->model->retrieveProductIdsBySkus($skus) + ); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php index d5ff3f580b457..4fb25b5927325 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/product_options_valid.xml b/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/product_options_valid.xml index 093521a0b7a49..5f418c4b177ad 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/product_options_valid.xml +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/product_options_valid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index 5042ac1b745cf..37f16ec4964dc 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -1,7 +1,7 @@ expects($this->any())->method('getWebsiteId')->willReturn('1'); $storeMock->expects($this->any())->method('getCode')->willReturn(\Magento\Store\Model\Store::ADMIN_CODE); $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); - $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->mediaGalleryProcessor = $this->getMock( \Magento\Catalog\Model\Product\Gallery\Processor::class, @@ -495,6 +495,7 @@ public function testGetBySkuFromCacheInitializedInGetById() public function testSaveExisting() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); $this->productFactoryMock->expects($this->any()) ->method('create') @@ -514,6 +515,7 @@ public function testSaveExisting() public function testSaveNew() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->at(0))->method('getIdBySku')->will($this->returnValue(null)); $this->resourceModelMock->expects($this->at(3))->method('getIdBySku')->will($this->returnValue(100)); $this->productFactoryMock->expects($this->any()) @@ -538,6 +540,7 @@ public function testSaveNew() */ public function testSaveUnableToSaveException() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->exactly(1))->method('getIdBySku')->will($this->returnValue(null)); $this->productFactoryMock->expects($this->exactly(2)) ->method('create') @@ -562,6 +565,7 @@ public function testSaveUnableToSaveException() */ public function testSaveException() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->exactly(1))->method('getIdBySku')->will($this->returnValue(null)); $this->productFactoryMock->expects($this->exactly(2)) ->method('create') @@ -587,6 +591,7 @@ public function testSaveException() */ public function testSaveInvalidProductException() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->exactly(1))->method('getIdBySku')->will($this->returnValue(null)); $this->productFactoryMock->expects($this->exactly(2)) ->method('create') @@ -610,6 +615,7 @@ public function testSaveInvalidProductException() */ public function testSaveThrowsTemporaryStateExceptionIfDatabaseConnectionErrorOccurred() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->productFactoryMock->expects($this->any()) ->method('create') ->will($this->returnValue($this->productMock)); @@ -709,6 +715,7 @@ public function testGetList() ->method('process') ->with($searchCriteriaMock, $collectionMock); $collectionMock->expects($this->once())->method('load'); + $collectionMock->expects($this->once())->method('addCategoryIds'); $collectionMock->expects($this->once())->method('getItems')->willReturn([$itemsMock]); $collectionMock->expects($this->once())->method('getSize')->willReturn(128); $searchResultsMock = $this->getMock( @@ -796,6 +803,7 @@ public function cacheKeyDataProvider() */ public function testSaveExistingWithOptions(array $newOptions, array $existingOptions, array $expectedData) { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); $this->productFactoryMock->expects($this->any()) ->method('create') @@ -964,6 +972,7 @@ public function saveExistingWithOptionsDataProvider() */ public function testSaveWithLinks(array $newLinks, array $existingLinks, array $expectedData) { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); $this->productFactoryMock->expects($this->any()) ->method('create') @@ -1143,30 +1152,37 @@ protected function setupProductMocksForSave() public function testSaveExistingWithNewMediaGalleryEntries() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $newEntriesData = [ - [ - "label" => "label_text", - 'position' => 10, - 'disabled' => false, - 'types' => ['image', 'small_image'], - 'content' => [ - ImageContentInterface::NAME => 'filename', - ImageContentInterface::TYPE => 'image/jpeg', - ImageContentInterface::BASE64_ENCODED_DATA => 'encoded_content', - ], - 'media_type' => 'media_type', - ], + 'images' => + [ + [ + 'value_id' => null, + 'label' => "label_text", + 'position' => 10, + 'disabled' => false, + 'types' => ['image', 'small_image'], + 'content' => [ + 'data' => [ + ImageContentInterface::NAME => 'filename', + ImageContentInterface::TYPE => 'image/jpeg', + ImageContentInterface::BASE64_ENCODED_DATA => 'encoded_content', + ], + ], + 'media_type' => 'media_type', + ] + ] ]; $this->setupProductMocksForSave(); //media gallery data - $this->productData['media_gallery_entries'] = $newEntriesData; + $this->productData['media_gallery'] = $newEntriesData; $this->extensibleDataObjectConverterMock ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->initializedProductMock->setData('media_gallery', []); + $this->initializedProductMock->setData('media_gallery', $newEntriesData); $this->initializedProductMock->expects($this->any()) ->method('getMediaAttributes') ->willReturn(["image" => "imageAttribute", "small_image" => "small_image_attribute"]); @@ -1222,12 +1238,52 @@ public function testSaveExistingWithNewMediaGalleryEntries() $this->model->save($this->productMock); } + public function websitesProvider() + { + return [ + [[1,2,3]] + ]; + } + + public function testSaveWithDifferentWebsites() + { + $storeMock = $this->getMock(StoreInterface::class); + $this->resourceModelMock->expects($this->at(0))->method('getIdBySku')->will($this->returnValue(null)); + $this->resourceModelMock->expects($this->at(3))->method('getIdBySku')->will($this->returnValue(100)); + $this->productFactoryMock->expects($this->any()) + ->method('create') + ->will($this->returnValue($this->productMock)); + $this->initializationHelperMock->expects($this->never())->method('initialize'); + $this->resourceModelMock->expects($this->once())->method('validate')->with($this->productMock) + ->willReturn(true); + $this->resourceModelMock->expects($this->once())->method('save')->with($this->productMock)->willReturn(true); + $this->extensibleDataObjectConverterMock + ->expects($this->once()) + ->method('toNestedArray') + ->will($this->returnValue($this->productData)); + $this->storeManagerMock->expects($this->any()) + ->method('getStore') + ->willReturn($storeMock); + $this->storeManagerMock->expects($this->once()) + ->method('getWebsites') + ->willReturn([ + 1 => ['first'], + 2 => ['second'], + 3 => ['third'] + ]); + $this->productMock->expects($this->once())->method('getWebsiteIds')->willReturn([1,2,3]); + $this->productMock->expects($this->once())->method('setWebsiteIds')->willReturn([2,3]); + + $this->assertEquals($this->productMock, $this->model->save($this->productMock)); + } + public function testSaveExistingWithMediaGalleryEntries() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); //update one entry, delete one entry $newEntries = [ [ - 'id' => 5, + 'value_id' => 5, "label" => "new_label_text", 'file' => 'filename1', 'position' => 10, @@ -1254,7 +1310,7 @@ public function testSaveExistingWithMediaGalleryEntries() $expectedResult = [ [ - 'id' => 5, + 'value_id' => 5, 'value_id' => 5, "label" => "new_label_text", 'file' => 'filename1', @@ -1271,7 +1327,7 @@ public function testSaveExistingWithMediaGalleryEntries() $this->setupProductMocksForSave(); //media gallery data - $this->productData['media_gallery_entries'] = $newEntries; + $this->productData['media_gallery']['images'] = $newEntries; $this->extensibleDataObjectConverterMock ->expects($this->once()) ->method('toNestedArray') diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php index dd858fa571ae1..2c44e0a50f983 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php @@ -1,6 +1,6 @@ [ - ['catalog_product_1'], + ['cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1]], ['id' => 1, 'name' => 'value', 'category_ids' => [1]], ], 'new product' => $this->getNewProductProviderData(), 'status and category change' => [ - [0 => 'catalog_product_1', 1 => 'catalog_category_product_1', 2 => 'catalog_category_product_2'], + [0 => 'cat_p_1', 1 => 'cat_c_p_1', 2 => 'cat_c_p_2'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 2], [ 'id' => 1, @@ -726,18 +726,18 @@ public function getIdentitiesProvider() ], ], 'status change only' => [ - [0 => 'catalog_product_1', 1 => 'catalog_category_product_7'], + [0 => 'cat_p_1', 1 => 'cat_c_p_7'], ['id' => 1, 'name' => 'value', 'category_ids' => [7], 'status' => 1], ['id' => 1, 'name' => 'value', 'category_ids' => [7], 'status' => 2], ], 'status changed, category unassigned' => $this->getStatusAndCategoryChangesData(), 'no status changes' => [ - [0 => 'catalog_product_1'], + [0 => 'cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], ], 'no stock status changes' => [ - [0 => 'catalog_product_1'], + [0 => 'cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], [ 'id' => 1, @@ -749,7 +749,7 @@ public function getIdentitiesProvider() ], ], 'no stock status data 1' => [ - [0 => 'catalog_product_1'], + [0 => 'cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], [ 'id' => 1, @@ -760,7 +760,7 @@ public function getIdentitiesProvider() ], ], 'no stock status data 2' => [ - [0 => 'catalog_product_1'], + [0 => 'cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], [ 'id' => 1, @@ -780,7 +780,7 @@ public function getIdentitiesProvider() private function getStatusAndCategoryChangesData() { return [ - [0 => 'catalog_product_1', 1 => 'catalog_category_product_5'], + [0 => 'cat_p_1', 1 => 'cat_c_p_5'], ['id' => 1, 'name' => 'value', 'category_ids' => [5], 'status' => 2], [ 'id' => 1, @@ -799,7 +799,7 @@ private function getStatusAndCategoryChangesData() private function getNewProductProviderData() { return [ - ['catalog_product_1', 'catalog_category_product_1'], + ['cat_p_1', 'cat_c_p_1'], null, [ 'id' => 1, @@ -818,7 +818,7 @@ private function getNewProductProviderData() private function getStatusStockProviderData($extensionAttributesMock) { return [ - [0 => 'catalog_product_1', 1 => 'catalog_category_product_1'], + [0 => 'cat_p_1', 1 => 'cat_c_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], [ 'id' => 1, @@ -831,20 +831,6 @@ private function getStatusStockProviderData($extensionAttributesMock) ]; } - public function testStatusAfterLoad() - { - $this->resource->expects($this->once())->method('load')->with($this->model, 1, null); - $this->eventManagerMock->expects($this->exactly(4))->method('dispatch'); - $this->model->load(1); - $this->assertEquals( - Status::STATUS_ENABLED, - $this->model->getData(\Magento\Catalog\Model\Product::STATUS) - ); - $this->assertFalse($this->model->hasDataChanges()); - $this->model->setStatus(Status::STATUS_DISABLED); - $this->assertTrue($this->model->hasDataChanges()); - } - /** * Test retrieving price Info */ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypeListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypeListTest.php index 63254a653ab35..6b0bea2c191f3 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypeListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypeListTest.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types.xml b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types.xml index 525beaf93c6e9..dc5284d1e5405 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types.xml +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types_merged.xml b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types_merged.xml index 96a8c06c1db2a..724203272620b 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types_merged.xml +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types_merged.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/ConfigTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/ConfigTest.php index af0c1625f9cb6..64b8b05b5755d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/ConfigTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/ConfigTest.php @@ -1,6 +1,6 @@ objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->readerMock = $this->getMock( \Magento\Catalog\Model\ProductTypes\Config\Reader::class, [], @@ -32,19 +43,35 @@ protected function setUp() false ); $this->cacheMock = $this->getMock(\Magento\Framework\Config\CacheInterface::class); + $this->serializerMock = $this->getMock(\Magento\Framework\Serialize\SerializerInterface::class); } /** - * @dataProvider getTypeDataProvider - * * @param array $value * @param mixed $expected + * @dataProvider getTypeDataProvider */ public function testGetType($value, $expected) { - $this->cacheMock->expects($this->any())->method('load')->will($this->returnValue(serialize($value))); - $this->model = new \Magento\Catalog\Model\ProductTypes\Config($this->readerMock, $this->cacheMock, 'cache_id'); - $this->assertEquals($expected, $this->model->getType('global')); + $this->cacheMock->expects($this->any()) + ->method('load') + ->willReturn('serializedData'); + + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->with('serializedData') + ->willReturn($value); + + $this->config = $this->objectManager->getObject( + \Magento\Catalog\Model\ProductTypes\Config::class, + [ + 'reader' => $this->readerMock, + 'cache' => $this->cacheMock, + 'cacheId' => 'cache_id', + 'serializer' => $this->serializerMock, + ] + ); + $this->assertEquals($expected, $this->config->getType('global')); } public function getTypeDataProvider() @@ -58,22 +85,43 @@ public function getTypeDataProvider() public function testGetAll() { $expected = ['Expected Data']; - $this->cacheMock->expects( - $this->once() - )->method( - 'load' - )->will( - $this->returnValue(serialize(['types' => $expected])) + $this->cacheMock->expects($this->once()) + ->method('load') + ->willReturn(json_encode('"types":["Expected Data"]]')); + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->willReturn(['types' => $expected]); + + $this->config = $this->objectManager->getObject( + \Magento\Catalog\Model\ProductTypes\Config::class, + [ + 'reader' => $this->readerMock, + 'cache' => $this->cacheMock, + 'cacheId' => 'cache_id', + 'serializer' => $this->serializerMock, + ] ); - $this->model = new \Magento\Catalog\Model\ProductTypes\Config($this->readerMock, $this->cacheMock, 'cache_id'); - $this->assertEquals($expected, $this->model->getAll()); + $this->assertEquals($expected, $this->config->getAll()); } public function testIsProductSet() { - $this->cacheMock->expects($this->once())->method('load')->will($this->returnValue(serialize([]))); - $this->model = new \Magento\Catalog\Model\ProductTypes\Config($this->readerMock, $this->cacheMock, 'cache_id'); + $this->cacheMock->expects($this->once()) + ->method('load') + ->willReturn(''); + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->willReturn([]); - $this->assertEquals(false, $this->model->isProductSet('typeId')); + $this->config = $this->objectManager->getObject( + \Magento\Catalog\Model\ProductTypes\Config::class, + [ + 'reader' => $this->readerMock, + 'cache' => $this->cacheMock, + 'cacheId' => 'cache_id', + 'serializer' => $this->serializerMock, + ] + ); + $this->assertEquals(false, $this->config->isProductSet('typeId')); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/AbstractTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/AbstractTest.php index 044bb6cfc9033..e7b7926edc562 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/AbstractTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/AbstractTest.php @@ -1,6 +1,6 @@ objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $entityFactory = $this->getMock(\Magento\Framework\Data\Collection\EntityFactory::class, [], [], '', false); $logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) ->disableOriginalConstructor() @@ -100,27 +123,39 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $entityMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\AbstractEntity::class) + $this->entityMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\AbstractEntity::class) ->disableOriginalConstructor() ->getMock(); + $this->galleryResourceMock = $this->getMockBuilder( + \Magento\Catalog\Model\ResourceModel\Product\Gallery::class + )->disableOriginalConstructor()->getMock(); + + $this->metadataPoolMock = $this->getMockBuilder( + \Magento\Framework\EntityManager\MetadataPool::class + )->disableOriginalConstructor()->getMock(); + + $this->galleryReadHandlerMock = $this->getMockBuilder( + \Magento\Catalog\Model\Product\Gallery\ReadHandler::class + )->disableOriginalConstructor()->getMock(); + $storeManager->expects($this->any())->method('getId')->willReturn(1); $storeManager->expects($this->any())->method('getStore')->willReturnSelf(); $universalFactory->expects($this->exactly(1))->method('create')->willReturnOnConsecutiveCalls( - $entityMock + $this->entityMock ); - $entityMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); - $entityMock->expects($this->once())->method('getDefaultAttributes')->willReturn([]); - $entityMock->expects($this->any())->method('getTable')->willReturnArgument(0); + $this->entityMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); + $this->entityMock->expects($this->once())->method('getDefaultAttributes')->willReturn([]); + $this->entityMock->expects($this->any())->method('getTable')->willReturnArgument(0); $this->connectionMock->expects($this->atLeastOnce())->method('select')->willReturn($this->selectMock); - $helper = new ObjectManager($this); - $this->prepareObjectManager([ - [\Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class, - $this->getMock(\Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class) - ] - ]); - $this->collection = $helper->getObject( + $productLimitationMock = $this->getMock( + \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class + ); + $productLimitationFactoryMock = $this->getMock(ProductLimitationFactory::class, ['create']); + $productLimitationFactoryMock->method('create') + ->willReturn($productLimitationMock); + $this->collection = $this->objectManager->getObject( \Magento\Catalog\Model\ResourceModel\Product\Collection::class, [ 'entityFactory' => $entityFactory, @@ -142,16 +177,28 @@ protected function setUp() 'customerSession' => $customerSession, 'dateTime' => $dateTime, 'groupManagement' => $groupManagement, - 'connection' => $this->connectionMock + 'connection' => $this->connectionMock, + 'productLimitationFactory' => $productLimitationFactoryMock, + 'metadataPool' => $this->metadataPoolMock, ] ); $this->collection->setConnection($this->connectionMock); + $this->objectManager->setBackwardCompatibleProperty( + $this->collection, + 'mediaGalleryResource', + $this->galleryResourceMock + ); + $this->objectManager->setBackwardCompatibleProperty( + $this->collection, + 'productGalleryReadHandler', + $this->galleryReadHandlerMock + ); } public function testAddProductCategoriesFilter() { - $condition = ['in' => [1,2]]; - $values = [1,2]; + $condition = ['in' => [1, 2]]; + $values = [1, 2]; $conditionType = 'nin'; $preparedSql = "category_id IN(1,2)"; $tableName = "catalog_category_product"; @@ -174,19 +221,45 @@ public function testAddProductCategoriesFilter() $this->collection->addCategoriesFilter([$conditionType => $values]); } - /** - * @param $map - */ - private function prepareObjectManager($map) + public function testAddMediaGalleryData() { - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManagerInterface::class); - $objectManagerMock->expects($this->any())->method('getInstance')->willReturnSelf(); - $objectManagerMock->expects($this->any()) - ->method('get') - ->will($this->returnValueMap($map)); - $reflectionClass = new \ReflectionClass(\Magento\Framework\App\ObjectManager::class); - $reflectionProperty = $reflectionClass->getProperty('_instance'); + $attributeId = 42; + $rowId = 4; + $linkField = 'row_id'; + $mediaGalleriesMock = [[$linkField => $rowId]]; + $itemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['getData']) + ->getMock(); + $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->disableOriginalConstructor() + ->getMock(); + $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $metadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->collection->addItem($itemMock); + $reflection = new \ReflectionClass(get_class($this->collection)); + $reflectionProperty = $reflection->getProperty('_isCollectionLoaded'); $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($objectManagerMock); + $reflectionProperty->setValue($this->collection, true); + + $this->galleryResourceMock->expects($this->once())->method('createBatchBaseSelect')->willReturn($selectMock); + $attributeMock->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->entityMock->expects($this->once())->method('getAttribute')->willReturn($attributeMock); + $itemMock->expects($this->atLeastOnce())->method('getData')->willReturn($rowId); + $selectMock->expects($this->once())->method('where')->with('entity.' . $linkField . ' IN (?)', [$rowId]); + $this->metadataPoolMock->expects($this->once())->method('getMetadata')->willReturn($metadataMock); + $metadataMock->expects($this->once())->method('getLinkField')->willReturn($linkField); + + $this->connectionMock->expects($this->once())->method('fetchAll')->with($selectMock)->willReturn( + [['row_id' => $rowId]] + ); + $this->galleryReadHandlerMock->expects($this->once())->method('addMediaDataToProduct') + ->with($itemMock, $mediaGalleriesMock); + + $this->assertSame($this->collection, $this->collection->addMediaGalleryData()); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CompositeBaseSelectProcessorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CompositeBaseSelectProcessorTest.php new file mode 100644 index 0000000000000..30060c81883eb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CompositeBaseSelectProcessorTest.php @@ -0,0 +1,54 @@ +objectManager = new ObjectManager($this); + } + + /** + * @expectedException \Magento\Framework\Exception\InputException + */ + public function testInitializeWithWrongProcessorInstance() + { + $processorValid = $this->getMock(BaseSelectProcessorInterface::class); + $processorInvalid = $this->getMock(\stdClass::class); + + $this->objectManager->getObject(CompositeBaseSelectProcessor::class, [ + 'baseSelectProcessors' => [$processorValid, $processorInvalid], + ]); + } + + public function testProcess() + { + $select = $this->getMockBuilder(Select::class)->disableOriginalConstructor()->getMock(); + + $processorFirst = $this->getMock(BaseSelectProcessorInterface::class); + $processorFirst->expects($this->once())->method('process')->with($select)->willReturn($select); + + $processorSecond = $this->getMock(BaseSelectProcessorInterface::class); + $processorSecond->expects($this->once())->method('process')->with($select)->willReturn($select); + + /** @var CompositeBaseSelectProcessor $baseSelectProcessors */ + $baseSelectProcessors = $this->objectManager->getObject(CompositeBaseSelectProcessor::class, [ + 'baseSelectProcessors' => [$processorFirst, $processorSecond], + ]); + $this->assertEquals($select, $baseSelectProcessors->process($select)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/FlatTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/FlatTest.php index a05a627693260..8fed73a4d007a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/FlatTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/FlatTest.php @@ -1,6 +1,6 @@ resource->deleteGalleryValueInStore($valueId, $entityId, $storeId); } + + public function testCountImageUses() + { + $results = [ + [ + 'value_id' => '1', + 'attribute_id' => 90, + 'value' => '/d/o/download_7.jpg', + 'media_type' => 'image', + 'disabled' => '0', + ], + ]; + + $this->connection->expects($this->once())->method('select')->will($this->returnValue($this->select)); + $this->select->expects($this->at(0))->method('from')->with( + [ + 'main' => 'table', + ], + '*' + )->willReturnSelf(); + $this->select->expects($this->at(1))->method('where')->with( + 'value = ?', + 1 + )->willReturnSelf(); + $this->connection->expects($this->once())->method('fetchAll') + ->with($this->select) + ->willReturn($results); + $this->assertEquals($this->resource->countImageUses(1), count($results)); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php index 3c92cde30012d..bb7250a904f61 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php @@ -1,12 +1,12 @@ objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->entityFactoryMock = $this->getMock( \Magento\Framework\Data\Collection\EntityFactory::class, [], @@ -133,14 +134,11 @@ function ($store) { $this->timezoneInterfaceMock = $this->getMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); $this->sessionMock = $this->getMock(\Magento\Customer\Model\Session::class, [], [], '', false); $this->dateTimeMock = $this->getMock(\Magento\Framework\Stdlib\DateTime::class); - $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->prepareObjectManager([ - [\Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class, - $this->getMock(\Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class) - ] - ]); + $productLimitationFactoryMock = $this->getMock(ProductLimitationFactory::class, ['create']); + $productLimitationFactoryMock->method('create') + ->willReturn($this->getMock(ProductLimitation::class)); - $this->collection = $this->objectManagerHelper->getObject( + $this->collection = $this->objectManager->getObject( \Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection::class, [ 'entityFactory' => $this->entityFactoryMock, @@ -160,7 +158,8 @@ function ($store) { 'catalogUrl' => $this->urlMock, 'localeDate' => $this->timezoneInterfaceMock, 'customerSession' => $this->sessionMock, - 'dateTime' => $this->dateTimeMock + 'dateTime' => $this->dateTimeMock, + 'productLimitationFactory' => $productLimitationFactoryMock, ] ); } @@ -175,20 +174,4 @@ public function testSetProduct() $this->collection->setProduct($product); $this->assertEquals(33, $this->collection->getStoreId()); } - - /** - * @param $map - */ - public function prepareObjectManager($map) - { - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManagerInterface::class); - $objectManagerMock->expects($this->any())->method('getInstance')->willReturnSelf(); - $objectManagerMock->expects($this->any()) - ->method('get') - ->will($this->returnValueMap($map)); - $reflectionClass = new \ReflectionClass(\Magento\Framework\App\ObjectManager::class); - $reflectionProperty = $reflectionClass->getProperty('_instance'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($objectManagerMock); - } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/LinkTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/LinkTest.php index 50f14246ed9ee..b9fb5a717c8c7 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/LinkTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/LinkTest.php @@ -1,6 +1,6 @@ objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->entityFactoryMock = $this->getMock( \Magento\Framework\Data\Collection\EntityFactory::class, ['create'], [], '', false ); @@ -147,11 +150,6 @@ protected function setUp() $this->metadataPoolMock->expects($this->any())->method('getMetadata')->willReturn($metadata); $this->selectMock->expects($this->exactly(2))->method('join'); - $this->prepareObjectManager([ - [\Magento\Framework\EntityManager\MetadataPool::class, $this->metadataPoolMock], - [\Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface::class, $this->joinProcessor] - ]); - $this->collection = new Collection( $this->entityFactoryMock, $this->loggerMock, @@ -160,7 +158,13 @@ protected function setUp() $this->optionsFactoryMock, $this->storeManagerMock, null, - $this->resourceMock + $this->resourceMock, + $this->metadataPoolMock + ); + $this->objectManager->setBackwardCompatibleProperty( + $this->collection, + 'joinProcessor', + $this->joinProcessor ); } @@ -168,20 +172,4 @@ public function testReset() { $this->collection->reset(); } - - /** - * @param $map - */ - private function prepareObjectManager($map) - { - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManagerInterface::class); - $objectManagerMock->expects($this->any())->method('getInstance')->willReturnSelf(); - $objectManagerMock->expects($this->any()) - ->method('get') - ->will($this->returnValueMap($map)); - $reflectionClass = new \ReflectionClass(\Magento\Framework\App\ObjectManager::class); - $reflectionProperty = $reflectionClass->getProperty('_instance'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($objectManagerMock); - } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/StatusBaseSelectProcessorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/StatusBaseSelectProcessorTest.php new file mode 100644 index 0000000000000..0a828a9106dff --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/StatusBaseSelectProcessorTest.php @@ -0,0 +1,129 @@ +eavConfig = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); + $this->metadataPool = $this->getMockBuilder(MetadataPool::class)->disableOriginalConstructor()->getMock(); + $this->storeResolver = $this->getMockBuilder(StoreResolverInterface::class)->getMock(); + $this->select = $this->getMockBuilder(Select::class)->disableOriginalConstructor()->getMock(); + + $this->statusBaseSelectProcessor = (new ObjectManager($this))->getObject(StatusBaseSelectProcessor::class, [ + 'eavConfig' => $this->eavConfig, + 'metadataPool' => $this->metadataPool, + 'storeResolver' => $this->storeResolver, + ]); + } + + public function testProcess() + { + $linkField = 'link_field'; + $backendTable = 'backend_table'; + $attributeId = 2; + $currentStoreId = 1; + + $metadata = $this->getMock(EntityMetadataInterface::class); + $metadata->expects($this->once()) + ->method('getLinkField') + ->willReturn($linkField); + $this->metadataPool->expects($this->once()) + ->method('getMetadata') + ->with(ProductInterface::class) + ->willReturn($metadata); + + /** @var AttributeInterface|\PHPUnit_Framework_MockObject_MockObject $statusAttribute */ + $statusAttribute = $this->getMockBuilder(AttributeInterface::class) + ->setMethods(['getBackendTable', 'getAttributeId']) + ->getMock(); + $statusAttribute->expects($this->atLeastOnce()) + ->method('getBackendTable') + ->willReturn($backendTable); + $statusAttribute->expects($this->atLeastOnce()) + ->method('getAttributeId') + ->willReturn($attributeId); + $this->eavConfig->expects($this->once()) + ->method('getAttribute') + ->with(Product::ENTITY, ProductInterface::STATUS) + ->willReturn($statusAttribute); + + $this->storeResolver->expects($this->once()) + ->method('getCurrentStoreId') + ->willReturn($currentStoreId); + + $this->select->expects($this->at(0)) + ->method('joinLeft') + ->with( + ['status_global_attr' => $backendTable], + "status_global_attr.{$linkField} = " + . BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS . ".{$linkField}" + . " AND status_global_attr.attribute_id = {$attributeId}" + . ' AND status_global_attr.store_id = ' . Store::DEFAULT_STORE_ID, + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(1)) + ->method('joinLeft') + ->with( + ['status_attr' => $backendTable], + "status_attr.{$linkField} = " . BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS . ".{$linkField}" + . " AND status_attr.attribute_id = {$attributeId}" + . " AND status_attr.store_id = {$currentStoreId}", + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(2)) + ->method('where') + ->with('IFNULL(status_attr.value, status_global_attr.value) = ?', Status::STATUS_ENABLED) + ->willReturnSelf(); + + $this->assertEquals($this->select, $this->statusBaseSelectProcessor->process($this->select)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Website/LinkTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Website/LinkTest.php index bedeba40eefcf..a01fbc4d2e386 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Website/LinkTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Website/LinkTest.php @@ -1,6 +1,6 @@ mediaConfig = $this->getMockBuilder(ConfigInterface::class)->getMockForAbstractClass(); + $this->mediaConfig->expects($this->any())->method('getBaseMediaPath')->willReturn('catalog/product'); + $this->mediaDirectory = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(); + $this->mediaDirectory->expects($this->once())->method('create')->with('catalog/product'); + $this->filesystem = $this->getMockBuilder(Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + $this->filesystem->expects($this->once()) + ->method('getDirectoryWrite') + ->with(DirectoryList::MEDIA) + ->willReturn($this->mediaDirectory); + $this->model = new Context( + $this->mediaConfig, + $this->filesystem + ); + } + + public function testGetPath() + { + $path = '/var/www/html/magento2ce/pub/media/catalog/product'; + $this->mediaDirectory->expects($this->once()) + ->method('getAbsolutePath') + ->with('catalog/product') + ->willReturn($path); + + $this->assertEquals($path, $this->model->getPath()); + } + + public function testGetUrl() + { + $baseUrl = 'http://localhost/pub/media/catalog/product'; + $this->mediaConfig->expects($this->once())->method('getBaseMediaUrl')->willReturn($baseUrl); + + $this->assertEquals($baseUrl, $this->model->getBaseUrl()); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php new file mode 100644 index 0000000000000..6c456a02c82b9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php @@ -0,0 +1,172 @@ +mediaConfig = $this->getMockBuilder(ConfigInterface::class)->getMockForAbstractClass(); + $this->encryptor = $this->getMockBuilder(EncryptorInterface::class)->getMockForAbstractClass(); + $this->imageContext = $this->getMockBuilder(ContextInterface::class)->getMockForAbstractClass(); + $this->model = new Image( + $this->mediaConfig, + $this->imageContext, + $this->encryptor, + '/somefile.png' + ); + } + + public function testModuleAndContentAndContentType() + { + $contentType = 'image'; + $this->assertEquals($contentType, $this->model->getContentType()); + $this->assertEquals($contentType, $this->model->getSourceContentType()); + $this->assertNull($this->model->getContent()); + $this->assertEquals('cache', $this->model->getModule()); + } + + public function testGetFilePath() + { + $this->assertEquals('/somefile.png', $this->model->getFilePath()); + } + + public function testGetSoureFile() + { + $this->mediaConfig->expects($this->once())->method('getBaseMediaPath')->willReturn('catalog/product'); + $this->assertEquals('catalog/product/somefile.png', $this->model->getSourceFile()); + } + + public function testGetContext() + { + $this->assertInstanceOf(ContextInterface::class, $this->model->getContext()); + } + + /** + * @param string $filePath + * @param array $miscParams + * @dataProvider getPathDataProvider + */ + public function testGetPath($filePath, $miscParams) + { + $imageModel = new Image( + $this->mediaConfig, + $this->imageContext, + $this->encryptor, + $filePath, + $miscParams + ); + $absolutePath = '/var/www/html/magento2ce/pub/media/catalog/product'; + $hashPath = md5(implode('_', $miscParams)); + $this->imageContext->expects($this->once())->method('getPath')->willReturn($absolutePath); + $this->encryptor->expects($this->once())->method('hash')->willReturn($hashPath); + $this->assertEquals( + $absolutePath . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . $hashPath . $filePath, + $imageModel->getPath() + ); + } + + /** + * @param string $filePath + * @param array $miscParams + * @dataProvider getPathDataProvider + */ + public function testGetNotUnixPath($filePath, $miscParams) + { + $imageModel = new Image( + $this->mediaConfig, + $this->imageContext, + $this->encryptor, + $filePath, + $miscParams + ); + $absolutePath = 'C:\www\magento2ce\pub\media\catalog\product'; + $hashPath = md5(implode('_', $miscParams)); + $this->imageContext->expects($this->once())->method('getPath')->willReturn($absolutePath); + $this->encryptor->expects($this->once())->method('hash')->willReturn($hashPath); + $this->assertEquals( + $absolutePath . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . $hashPath . $filePath, + $imageModel->getPath() + ); + } + + /** + * @param string $filePath + * @param array $miscParams + * @dataProvider getPathDataProvider + */ + public function testGetUrl($filePath, $miscParams) + { + $imageModel = new Image( + $this->mediaConfig, + $this->imageContext, + $this->encryptor, + $filePath, + $miscParams + ); + $absolutePath = 'http://localhost/pub/media/catalog/product'; + $hashPath = md5(implode('_', $miscParams)); + $this->imageContext->expects($this->once())->method('getBaseUrl')->willReturn($absolutePath); + $this->encryptor->expects($this->once())->method('hash')->willReturn($hashPath); + $this->assertEquals( + $absolutePath . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . $hashPath . $filePath, + $imageModel->getUrl() + ); + } + + public function getPathDataProvider() + { + return [ + [ + '/some_file.png', + [], //default value for miscParams + ], + [ + '/some_file_2.png', + [ + 'image_type' => 'thumbnail', + 'image_height' => 75, + 'image_width' => 75, + 'keep_aspect_ratio' => 'proportional', + 'keep_frame' => 'frame', + 'keep_transparency' => 'transparency', + 'constrain_only' => 'doconstrainonly', + 'background' => 'ffffff', + 'angle' => null, + 'quality' => 80, + ], + ] + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/PlaceholderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/PlaceholderTest.php new file mode 100644 index 0000000000000..ec45cd6f72127 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/PlaceholderTest.php @@ -0,0 +1,163 @@ +scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class)->getMockForAbstractClass(); + $this->imageContext = $this->getMockBuilder(ContextInterface::class)->getMockForAbstractClass(); + $this->repository = $this->getMockBuilder(Repository::class)->disableOriginalConstructor()->getMock(); + $this->model = new Placeholder( + $this->imageContext, + $this->scopeConfig, + $this->repository, + 'thumbnail' + ); + } + + public function testModuleAndContentAndContentType() + { + $contentType = 'image'; + $this->assertEquals($contentType, $this->model->getContentType()); + $this->assertEquals($contentType, $this->model->getSourceContentType()); + $this->assertNull($this->model->getContent()); + $this->assertEquals('placeholder', $this->model->getModule()); + } + + public function testGetFilePath() + { + $this->assertNull($this->model->getFilePath()); + $this->scopeConfig->expects($this->once())->method('getValue')->willReturn('default/thumbnail.jpg'); + $this->assertEquals('default/thumbnail.jpg', $this->model->getFilePath()); + } + + public function testGetContext() + { + $this->assertInstanceOf(ContextInterface::class, $this->model->getContext()); + } + + /** + * @param string $imageType + * @param string $placeholderPath + * @dataProvider getPathDataProvider + */ + public function testGetPathAndGetSourceFile($imageType, $placeholderPath) + { + $imageModel = new Placeholder( + $this->imageContext, + $this->scopeConfig, + $this->repository, + $imageType + ); + $absolutePath = '/var/www/html/magento2ce/pub/media/catalog/product'; + + $this->scopeConfig->expects($this->any()) + ->method('getValue') + ->with( + "catalog/placeholder/{$imageType}_placeholder", + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + null + )->willReturn($placeholderPath); + + if ($placeholderPath == null) { + $this->imageContext->expects($this->never())->method('getPath'); + $assetMock = $this->getMockBuilder(\Magento\Framework\View\Asset\MergeableInterface::class) + ->getMockForAbstractClass(); + $expectedResult = 'path/to_default/placeholder/by_type'; + $assetMock->expects($this->any())->method('getSourceFile')->willReturn($expectedResult); + $this->repository->expects($this->any())->method('createAsset')->willReturn($assetMock); + } else { + $this->imageContext->expects($this->any())->method('getPath')->willReturn($absolutePath); + $expectedResult = $absolutePath + . DIRECTORY_SEPARATOR . $imageModel->getModule() + . DIRECTORY_SEPARATOR . $placeholderPath; + } + + $this->assertEquals($expectedResult, $imageModel->getPath()); + $this->assertEquals($expectedResult, $imageModel->getSourceFile()); + } + + /** + * @param string $imageType + * @param string $placeholderPath + * @dataProvider getPathDataProvider + */ + public function testGetUrl($imageType, $placeholderPath) + { + $imageModel = new Placeholder( + $this->imageContext, + $this->scopeConfig, + $this->repository, + $imageType + ); + + $this->scopeConfig->expects($this->any()) + ->method('getValue') + ->with( + "catalog/placeholder/{$imageType}_placeholder", + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + null + )->willReturn($placeholderPath); + + if ($placeholderPath == null) { + $this->imageContext->expects($this->never())->method('getBaseUrl'); + $expectedResult = 'http://localhost/pub/media/catalog/product/to_default/placeholder/by_type'; + $this->repository->expects($this->any())->method('getUrl')->willReturn($expectedResult); + } else { + $baseUrl = 'http://localhost/pub/media/catalog/product'; + $this->imageContext->expects($this->any())->method('getBaseUrl')->willReturn($baseUrl); + $expectedResult = $baseUrl + . DIRECTORY_SEPARATOR . $imageModel->getModule() + . DIRECTORY_SEPARATOR . $placeholderPath; + } + + $this->assertEquals($expectedResult, $imageModel->getUrl()); + } + + public function getPathDataProvider() + { + return [ + [ + 'thumbnail', + 'default/thumbnail.jpg', + ], + [ + 'non_exist', + null, + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/_files/converted_view.php b/app/code/Magento/Catalog/Test/Unit/Model/_files/converted_view.php index 49d865253ced4..d173b9dc05a32 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/_files/converted_view.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/_files/converted_view.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/Test/Unit/Observer/MenuCategoryDataTest.php b/app/code/Magento/Catalog/Test/Unit/Observer/MenuCategoryDataTest.php index fde1c74bc5ae5..a935861a263e1 100644 --- a/app/code/Magento/Catalog/Test/Unit/Observer/MenuCategoryDataTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Observer/MenuCategoryDataTest.php @@ -1,6 +1,6 @@ cache = $this->getMock(\Magento\Framework\App\CacheInterface::class); $this->cacheState = $this->getMock(\Magento\Framework\App\Cache\StateInterface::class); + $this->serializer = $this->getMock(SerializerInterface::class); $this->subject = $this->getMock(\Magento\Catalog\Model\ResourceModel\Config::class, [], [], '', false); } @@ -47,12 +50,17 @@ public function testGetAttributesUsedInListingFromCache() $entityTypeId = 'type'; $storeId = 'store'; $attributes = ['attributes']; + $serializedAttributes = '["attributes"]'; $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn(serialize($attributes)); + $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn($serializedAttributes); + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($serializedAttributes) + ->willReturn($attributes); $this->assertEquals( $attributes, @@ -68,14 +76,21 @@ public function testGetAttributesUsedInListingWithCacheSave() $entityTypeId = 'type'; $storeId = 'store'; $attributes = ['attributes']; + $serializedAttributes = '["attributes"]'; $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn(false); + $this->serializer->expects($this->never()) + ->method('unserialize'); + $this->serializer->expects($this->once()) + ->method('serialize') + ->with($attributes) + ->willReturn($serializedAttributes); $this->cache->expects($this->any())->method('save')->with( - serialize($attributes), + $serializedAttributes, $cacheId, [ \Magento\Eav\Model\Cache\Type::CACHE_TAG, @@ -110,11 +125,16 @@ public function testGetAttributesUsedForSortByFromCache() $entityTypeId = 'type'; $storeId = 'store'; $attributes = ['attributes']; + $serializedAttributes = '["attributes"]'; $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; - $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn(serialize($attributes)); + $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn($serializedAttributes); + $this->serializer->expects($this->once()) + ->method('unserialize') + ->with($serializedAttributes) + ->willReturn($attributes); $this->assertEquals( $attributes, @@ -130,13 +150,20 @@ public function testGetAttributesUsedForSortByWithCacheSave() $entityTypeId = 'type'; $storeId = 'store'; $attributes = ['attributes']; + $serializedAttributes = '["attributes"]'; $this->subject->expects($this->any())->method('getEntityTypeId')->willReturn($entityTypeId); $this->subject->expects($this->any())->method('getStoreId')->willReturn($storeId); $cacheId = \Magento\Catalog\Plugin\Model\ResourceModel\Config::PRODUCT_LISTING_SORT_BY_ATTRIBUTES_CACHE_ID . $entityTypeId . '_' . $storeId; $this->cache->expects($this->any())->method('load')->with($cacheId)->willReturn(false); + $this->serializer->expects($this->never()) + ->method('unserialize'); + $this->serializer->expects($this->once()) + ->method('serialize') + ->with($attributes) + ->willReturn($serializedAttributes); $this->cache->expects($this->any())->method('save')->with( - serialize($attributes), + $serializedAttributes, $cacheId, [ \Magento\Eav\Model\Cache\Type::CACHE_TAG, @@ -165,7 +192,8 @@ protected function getConfig($cacheEnabledFlag) \Magento\Catalog\Plugin\Model\ResourceModel\Config::class, [ 'cache' => $this->cache, - 'cacheState' => $this->cacheState + 'cacheState' => $this->cacheState, + 'serializer' => $this->serializer, ] ); } diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/BasePriceTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/BasePriceTest.php index 3cb1749dc4add..11ff04d0aa0fb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/BasePriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/BasePriceTest.php @@ -1,6 +1,6 @@ price = $this->getMock(TierPrice::class, [], [], '', false); + $this->priceInfo = $this->getMockForAbstractClass(PriceInfoInterface::class); + $this->saleable = $this->getMockForAbstractClass(SaleableInterface::class); + + $this->objectManager = new ObjectManager($this); + + $this->calculator = $this->getMockForAbstractClass(CalculatorInterface::class); + $this->object = $this->objectManager->getObject( + MinimalTierPriceCalculator::class, + ['calculator' => $this->calculator] + ); + } + + private function getValueTierPricesExistShouldReturnMinTierPrice() + { + $minPrice = 5; + $notMinPrice = 10; + + $minAmount = $this->getMockForAbstractClass(AmountInterface::class); + $minAmount->expects($this->once())->method('getValue')->willReturn($minPrice); + + $notMinAmount = $this->getMockForAbstractClass(AmountInterface::class); + $notMinAmount->expects($this->once())->method('getValue')->willReturn($notMinPrice); + + $tierPriceList = [ + [ + 'price' => $minAmount + ], + [ + 'price' => $notMinAmount + ] + ]; + + $this->price->expects($this->once())->method('getTierPriceList')->willReturn($tierPriceList); + + $this->priceInfo->expects($this->once())->method('getPrice')->with(TierPrice::PRICE_CODE) + ->willReturn($this->price); + + $this->saleable->expects($this->once())->method('getPriceInfo')->willReturn($this->priceInfo); + return $minPrice; + } + + public function testGetValueTierPricesExistShouldReturnMinTierPrice() + { + $minPrice = $this->getValueTierPricesExistShouldReturnMinTierPrice(); + $this->assertEquals($minPrice, $this->object->getValue($this->saleable)); + } + + public function testGetGetAmountMinTierPriceExistShouldReturnAmountObject() + { + $minPrice = $this->getValueTierPricesExistShouldReturnMinTierPrice(); + + $amount = $this->getMockForAbstractClass(AmountInterface::class); + + $this->calculator->expects($this->once()) + ->method('getAmount') + ->with($minPrice, $this->saleable) + ->willReturn($amount); + + $this->assertSame($amount, $this->object->getAmount($this->saleable)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/RegularPriceTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/RegularPriceTest.php index 09988088c1632..54259101469bd 100644 --- a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/RegularPriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/RegularPriceTest.php @@ -1,6 +1,6 @@ product = $this->getMock( \Magento\Catalog\Model\Product::class, - ['getPriceInfo', '__wakeup', 'getCanShowPrice'], + ['getPriceInfo', '__wakeup', 'getCanShowPrice', 'isSalable'], [], '', false @@ -78,9 +94,7 @@ protected function setUp() $this->priceBox = $this->getMock(\Magento\Framework\Pricing\Render\PriceBox::class, [], [], '', false); $this->logger = $this->getMock(\Psr\Log\LoggerInterface::class); - $this->layout->expects($this->any()) - ->method('getBlock') - ->will($this->returnValue($this->priceBox)); + $this->layout->expects($this->any())->method('getBlock')->willReturn($this->priceBox); $cacheState = $this->getMockBuilder(\Magento\Framework\App\Cache\StateInterface::class) ->getMockForAbstractClass(); @@ -93,12 +107,9 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $urlBuilder = $this->getMockBuilder(\Magento\Framework\UrlInterface::class) - ->getMockForAbstractClass(); - - $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->getMockForAbstractClass(); + $urlBuilder = $this->getMockBuilder(\Magento\Framework\UrlInterface::class)->getMockForAbstractClass(); + $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)->getMockForAbstractClass(); $storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) ->setMethods(['getStore', 'getCode']) ->getMockForAbstractClass(); @@ -144,6 +155,11 @@ protected function setUp() ->will($this->returnValue(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->salableResolverMock = $this->getMockBuilder(SalableResolverInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->minimalPriceCalculator = $this->getMockForAbstractClass(MinimalPriceCalculatorInterface::class); $this->object = $objectManager->getObject( \Magento\Catalog\Pricing\Render\FinalPriceBox::class, [ @@ -151,7 +167,9 @@ protected function setUp() 'saleableItem' => $this->product, 'rendererPool' => $this->rendererPool, 'price' => $this->price, - 'data' => ['zone' => 'test_zone', 'list_category_page' => true] + 'data' => ['zone' => 'test_zone', 'list_category_page' => true], + 'salableResolver' => $this->salableResolverMock, + 'minimalPriceCalculator' => $this->minimalPriceCalculator ] ); } @@ -169,6 +187,8 @@ public function testRenderMsrpDisabled() ->with($this->equalTo($this->product)) ->will($this->returnValue(false)); + $this->salableResolverMock->expects($this->once())->method('isSalable')->with($this->product)->willReturn(true); + $result = $this->object->toHtml(); //assert price wrapper @@ -177,6 +197,18 @@ public function testRenderMsrpDisabled() $this->assertRegExp('/[final_price]/', $result); } + public function testNotSalableItem() + { + $this->salableResolverMock + ->expects($this->once()) + ->method('isSalable') + ->with($this->product) + ->willReturn(false); + $result = $this->object->toHtml(); + + $this->assertEmpty($result); + } + public function testRenderMsrpEnabled() { $priceType = $this->getMock(\Magento\Msrp\Pricing\Price\MsrpPrice::class, [], [], '', false); @@ -211,6 +243,8 @@ public function testRenderMsrpEnabled() ->with('msrp_price', $this->product, $arguments) ->will($this->returnValue($priceBoxRender)); + $this->salableResolverMock->expects($this->once())->method('isSalable')->with($this->product)->willReturn(true); + $result = $this->object->toHtml(); //assert price wrapper @@ -230,6 +264,8 @@ public function testRenderMsrpNotRegisteredException() ->with($this->equalTo('msrp_price')) ->will($this->throwException(new \InvalidArgumentException())); + $this->salableResolverMock->expects($this->once())->method('isSalable')->with($this->product)->willReturn(true); + $result = $this->object->toHtml(); //assert price wrapper @@ -240,11 +276,17 @@ public function testRenderMsrpNotRegisteredException() public function testRenderAmountMinimal() { - $priceType = $this->getMock(\Magento\Catalog\Pricing\Price\FinalPrice::class, [], [], '', false); - $amount = $this->getMockForAbstractClass(\Magento\Framework\Pricing\Amount\AmountInterface::class); $priceId = 'price_id'; $html = 'html'; + $this->object->setData('price_id', $priceId); + $this->product->expects($this->never())->method('getId'); + + $amount = $this->getMockForAbstractClass(AmountInterface::class); + + $this->minimalPriceCalculator->expects($this->once())->method('getAmount') + ->with($this->product) + ->willReturn($amount); $arguments = [ 'zone' => 'test_zone', @@ -255,24 +297,15 @@ public function testRenderAmountMinimal() 'skip_adjustments' => true, ]; - $amountRender = $this->getMock(\Magento\Framework\Pricing\Render\Amount::class, ['toHtml'], [], '', false); + $amountRender = $this->getMock(Amount::class, ['toHtml'], [], '', false); $amountRender->expects($this->once()) ->method('toHtml') - ->will($this->returnValue($html)); - - $this->priceInfo->expects($this->once()) - ->method('getPrice') - ->with(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE) - ->will($this->returnValue($priceType)); - - $priceType->expects($this->once()) - ->method('getMinimalPrice') - ->will($this->returnValue($amount)); + ->willReturn($html); $this->rendererPool->expects($this->once()) ->method('createAmountRender') ->with($amount, $this->product, $this->price, $arguments) - ->will($this->returnValue($amountRender)); + ->willReturn($amountRender); $this->assertEquals($html, $this->object->renderAmountMinimal()); } @@ -327,36 +360,29 @@ public function hasSpecialPriceProvider() public function testShowMinimalPrice() { - $finalPrice = 10.0; $minimalPrice = 5.0; - $displayMininmalPrice = 2.0; - - $this->object->setDisplayMinimalPrice($displayMininmalPrice); - - $finalPriceType = $this->getMock(\Magento\Catalog\Pricing\Price\FinalPrice::class, [], [], '', false); + $finalPrice = 10.0; + $displayMininmalPrice = true; - $finalPriceAmount = $this->getMockForAbstractClass(\Magento\Framework\Pricing\Amount\AmountInterface::class); - $minimalPriceAmount = $this->getMockForAbstractClass(\Magento\Framework\Pricing\Amount\AmountInterface::class); + $this->minimalPriceCalculator->expects($this->once())->method('getValue')->with($this->product) + ->willReturn($minimalPrice); + $finalPriceAmount = $this->getMockForAbstractClass(AmountInterface::class); $finalPriceAmount->expects($this->once()) ->method('getValue') ->will($this->returnValue($finalPrice)); - $minimalPriceAmount->expects($this->once()) - ->method('getValue') - ->will($this->returnValue($minimalPrice)); - $finalPriceType->expects($this->at(0)) + $finalPriceType = $this->getMock(FinalPrice::class, [], [], '', false); + $finalPriceType->expects($this->once()) ->method('getAmount') ->will($this->returnValue($finalPriceAmount)); - $finalPriceType->expects($this->at(1)) - ->method('getMinimalPrice') - ->will($this->returnValue($minimalPriceAmount)); $this->priceInfo->expects($this->once()) ->method('getPrice') - ->with(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE) - ->will($this->returnValue($finalPriceType)); + ->with(FinalPrice::PRICE_CODE) + ->willReturn($finalPriceType); + $this->object->setDisplayMinimalPrice($displayMininmalPrice); $this->assertTrue($this->object->showMinimalPrice()); } diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Render/PriceBoxTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Render/PriceBoxTest.php index 1e8b930a0c491..1b7cc7926da62 100644 --- a/app/code/Magento/Catalog/Test/Unit/Pricing/Render/PriceBoxTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Render/PriceBoxTest.php @@ -1,6 +1,6 @@ [ - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions::DATA_SOURCE_DEFAULT => [ + CustomOptions::DATA_SOURCE_DEFAULT => [ 'title' => 'original' ] ] ]; $options = [ - $this->getProductOptionMock(['title' => 'option1']), + $this->getProductOptionMock(['title' => 'option1', 'store_title' => 'Option Store Title']), $this->getProductOptionMock( - ['title' => 'option2'], + ['title' => 'option2', 'store_title' => null], [ - $this->getProductOptionMock(['title' => 'value1']), - $this->getProductOptionMock(['title' => 'value2']) + $this->getProductOptionMock(['title' => 'value1', 'store_title' => 'Option Value Store Title']), + $this->getProductOptionMock(['title' => 'value2', 'store_title' => null]) ] ) ]; $resultData = [ $productId => [ - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions::DATA_SOURCE_DEFAULT => [ - 'title' => 'original', - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions::FIELD_ENABLE => 1, - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions::GRID_OPTIONS_NAME => [ - ['title' => 'option1'], + CustomOptions::DATA_SOURCE_DEFAULT => [ + CustomOptions::FIELD_TITLE_NAME => 'original', + CustomOptions::FIELD_ENABLE => 1, + CustomOptions::GRID_OPTIONS_NAME => [ [ - 'title' => 'option2', + CustomOptions::FIELD_TITLE_NAME => 'option1', + CustomOptions::FIELD_STORE_TITLE_NAME => 'Option Store Title', + CustomOptions::FIELD_IS_USE_DEFAULT => false + ], [ + CustomOptions::FIELD_TITLE_NAME => 'option2', + CustomOptions::FIELD_STORE_TITLE_NAME => null, + CustomOptions::FIELD_IS_USE_DEFAULT => true, CustomOptions::GRID_TYPE_SELECT_NAME => [ - ['title' => 'value1'], - ['title' => 'value2'] + [ + CustomOptions::FIELD_TITLE_NAME => 'value1', + CustomOptions::FIELD_STORE_TITLE_NAME => 'Option Value Store Title', + CustomOptions::FIELD_IS_USE_DEFAULT => false + ], [ + CustomOptions::FIELD_TITLE_NAME => 'value2', + CustomOptions::FIELD_STORE_TITLE_NAME => null, + CustomOptions::FIELD_IS_USE_DEFAULT => true + ] ] ] ] @@ -154,13 +166,13 @@ public function testModifyMeta() */ protected function getProductOptionMock(array $data, array $values = []) { + /** @var ProductOption|\PHPUnit_Framework_MockObject_MockObject $productOptionMock */ $productOptionMock = $this->getMockBuilder(ProductOption::class) ->disableOriginalConstructor() + ->setMethods(['getValues']) ->getMock(); - $productOptionMock->expects($this->any()) - ->method('getData') - ->willReturn($data); + $productOptionMock->setData($data); $productOptionMock->expects($this->any()) ->method('getValues') ->willReturn($values); diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php index 2deb4800bd023..c92424fbebbf4 100755 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -1,6 +1,6 @@ assertSame($this->getSampleData(), $this->getModel()->modifyData($this->getSampleData())); + $this->productMock->expects($this->once())->method('getId')->willReturn(2051); + $actualResult = $this->getModel()->modifyData($this->getSampleData()); + $this->assertSame("", $actualResult[2051]['product']['media_gallery']['images'][0]['label']); } public function testModifyMeta() @@ -40,4 +42,24 @@ public function testModifyMeta() $this->assertSame([], $this->getModel()->modifyMeta($meta)); } + + /** + * {@inheritdoc} + */ + protected function getSampleData() + { + return [ + 2051 => [ + 'product' => [ + 'media_gallery' => [ + 'images' => [ + [ + 'label' => null + ] + ] + ] + ] + ] + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/RelatedTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/RelatedTest.php index b0b2365c4f151..8bfcc0edd196e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/RelatedTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/RelatedTest.php @@ -1,6 +1,6 @@ productPriceOptions = $this->getMock(ProductPriceOptionsInterface::class); + $this->arrayManager = $this->getMock(ArrayManager::class, [], [], '', false); + + $this->tierPrice = (new ObjectManager($this))->getObject(TierPrice::class, [ + 'productPriceOptions' => $this->productPriceOptions, + 'arrayManager' => $this->arrayManager, + ]); + } + + /** + * Test modifyData. + */ + public function testModifyData() + { + $data = [1, 2]; + $this->assertEquals($data, $this->tierPrice->modifyData($data)); + } + + /** + * Test modifyMeta. + */ + public function testModifyMeta() + { + $meta = [1, 2]; + $tierPricePath = 'tier_price'; + $priceWrapperPath = 'tier_price/some-wrapper'; + $pricePath = $priceWrapperPath . '/price'; + $priceMeta = [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'visible' => true, + 'validation' => ['validate-number' => true], + ], + ], + ], + ]; + + $this->productPriceOptions->expects($this->once())->method('toOptionArray')->willReturn([ + [ + 'value' => ProductPriceOptionsInterface::VALUE_FIXED, + 'label' => 'label1', + ], + ]); + + $this->productPriceOptions->expects($this->once())->method('toOptionArray')->willReturn([ + [ + 'value' => ProductPriceOptionsInterface::VALUE_FIXED, + 'label' => 'label1', + ], + ]); + + $this->arrayManager + ->expects($this->exactly(2)) + ->method('findPath') + ->willReturnMap([ + [ + ProductAttributeInterface::CODE_TIER_PRICE, + $meta, + null, + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $tierPricePath + ], + [ + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE, + $meta, + $tierPricePath, + null, + ArrayManager::DEFAULT_PATH_DELIMITER, + $pricePath + ], + ]); + $this->arrayManager + ->expects($this->once()) + ->method('get') + ->with($pricePath, $meta) + ->willReturn($priceMeta); + $this->arrayManager + ->expects($this->once()) + ->method('remove') + ->with($pricePath, $meta) + ->willReturn($meta); + $this->arrayManager + ->expects($this->once()) + ->method('slicePath') + ->with($pricePath, 0, -1) + ->willReturn($priceWrapperPath); + $this->arrayManager + ->expects($this->once()) + ->method('merge') + ->with($priceWrapperPath, $meta, $this->isType('array')) + ->willReturnArgument(2); + + $modifiedMeta = $this->tierPrice->modifyMeta($meta); + $children = $modifiedMeta['price_value']['children']; + + $this->assertNotEmpty($children[ProductAttributeInterface::CODE_TIER_PRICE_FIELD_VALUE_TYPE]); + $this->assertNotEmpty($children[ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PERCENTAGE_VALUE]); + $this->assertEquals($priceMeta, $children[ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE]); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php index deaadb0f12f95..a6b2b82fe6bcc 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php @@ -1,6 +1,6 @@ [ 'config' => [ 'componentType' => 'dynamicRows', + 'component' => 'Magento_Catalog/js/components/dynamic-rows-tier-price', 'label' => __('Customer Group Price'), 'renderDefaultRecord' => false, 'recordTemplate' => 'record', @@ -493,7 +494,6 @@ private function getTierPriceStructure($tierPricePath) 'data' => [ 'config' => [ 'componentType' => Field::NAME, - 'component' => 'Magento_Catalog/js/form/element/price-input', 'formElement' => Input::NAME, 'dataType' => Price::NAME, 'label' => __('Price'), diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php index 9ca34ff764dcd..d5aab5340f771 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php @@ -1,6 +1,6 @@ locator = $locator; $this->categoryCollectionFactory = $categoryCollectionFactory; $this->dbHelper = $dbHelper; $this->urlBuilder = $urlBuilder; $this->arrayManager = $arrayManager; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); } /** @@ -286,7 +295,7 @@ protected function getCategoriesTree($filter = null) { $categoryTree = $this->getCacheManager()->load(self::CATEGORY_TREE_ID . '_' . $filter); if ($categoryTree) { - return unserialize($categoryTree); + return $this->serializer->unserialize($categoryTree); } $storeId = $this->locator->getStore()->getId(); @@ -340,7 +349,7 @@ protected function getCategoriesTree($filter = null) } $this->getCacheManager()->save( - serialize($categoryById[CategoryModel::TREE_ROOT_ID]['optgroup']), + $this->serializer->serialize($categoryById[CategoryModel::TREE_ROOT_ID]['optgroup']), self::CATEGORY_TREE_ID . '_' . $filter, [ \Magento\Catalog\Model\Category::CACHE_TAG, diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php index 5e6c88a1ba93d..9041dcc0380b6 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php @@ -1,6 +1,6 @@ $option) { - $options[$index] = $this->formatPriceByPath(static::FIELD_PRICE_NAME, $option->getData()); + $optionData = $option->getData(); + $optionData[static::FIELD_IS_USE_DEFAULT] = !$option->getData(static::FIELD_STORE_TITLE_NAME); + $options[$index] = $this->formatPriceByPath(static::FIELD_PRICE_NAME, $optionData); $values = $option->getValues() ?: []; + foreach ($values as $value) { + $value->setData(static::FIELD_IS_USE_DEFAULT, !$value->getData(static::FIELD_STORE_TITLE_NAME)); + } /** @var \Magento\Catalog\Model\Product\Option $value */ foreach ($values as $value) { $options[$index][static::GRID_TYPE_SELECT_NAME][] = $this->formatPriceByPath( @@ -529,7 +536,8 @@ protected function getCommonContainerConfig($sortOrder) 'component' => 'Magento_Catalog/component/static-type-input', 'valueUpdate' => 'input', 'imports' => [ - 'optionId' => '${ $.provider }:${ $.parentScope }.option_id' + 'optionId' => '${ $.provider }:${ $.parentScope }.option_id', + 'isUseDefault' => '${ $.provider }:${ $.parentScope }.is_use_default' ] ], ], @@ -604,6 +612,7 @@ protected function getSelectTypeGridConfig($sortOrder) 'imports' => [ 'optionId' => '${ $.provider }:${ $.parentScope }.option_id', 'optionTypeId' => '${ $.provider }:${ $.parentScope }.option_type_id', + 'isUseDefault' => '${ $.provider }:${ $.parentScope }.is_use_default' ], 'service' => [ 'template' => 'Magento_Catalog/form/element/helper/custom-option-type-service', @@ -1109,7 +1118,7 @@ private function getLocaleCurrency() } return $this->localeCurrency; } - + /** * Format price according to the locale of the currency * diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index 1419c17e11b29..687f6349146c2 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -1,6 +1,6 @@ false, - 'add_widgets' => false + 'add_widgets' => false, + 'add_directives' => true, + 'use_container' => true, + 'container_class' => 'hor-scroll', ]; return $meta; diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index 0fbba264f7e93..8c4dac63e21eb 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -1,6 +1,6 @@ $this->locator->getStore()->getConfig('catalog/fields_masks/' . $listener), 'component' => 'Magento_Catalog/js/components/import-handler', - 'imports' => [ - 'handleNameChanges' => '${$.provider}:data.product.name', - 'handleDescriptionChanges' => '${$.provider}:data.product.description', - 'handleSkuChanges' => '${$.provider}:data.product.sku', - 'handleColorChanges' => '${$.provider}:data.product.color', - 'handleCountryChanges' => '${$.provider}:data.product.country_of_manufacture', - 'handleGenderChanges' => '${$.provider}:data.product.gender', - 'handleMaterialChanges' => '${$.provider}:data.product.material', - 'handleShortDescriptionChanges' => '${$.provider}:data.product.short_description', - 'handleSizeChanges' => '${$.provider}:data.product.size' - ], 'allowImport' => !$this->locator->getProduct()->getId(), ]; diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Images.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Images.php index 810a06df4a42f..e7b3e86083a8e 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Images.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Images.php @@ -1,6 +1,6 @@ locator->getProduct(); + $modelId = $product->getId(); + if ( + isset($data[$modelId][self::DATA_SOURCE_DEFAULT]['media_gallery']) + && !empty($data[$modelId][self::DATA_SOURCE_DEFAULT]['media_gallery']) + && !empty($data[$modelId][self::DATA_SOURCE_DEFAULT]['media_gallery']['images']) + ) { + foreach ($data[$modelId][self::DATA_SOURCE_DEFAULT]['media_gallery']['images'] as $index => $image) { + if (!isset($image['label'])) { + $data[$modelId][self::DATA_SOURCE_DEFAULT]['media_gallery']['images'][$index]['label'] = ""; + } + } + }; + return $data; } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Related.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Related.php index 9c488cbebe11a..a9f14444a6b09 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Related.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Related.php @@ -1,6 +1,6 @@ productPriceOptions = $productPriceOptions; + $this->arrayManager = $arrayManager; + } + + /** + * {@inheritdoc} + */ + public function modifyData(array $data) + { + return $data; + } + + /** + * {@inheritdoc} + */ + public function modifyMeta(array $meta) + { + $tierPricePath = $this->arrayManager->findPath( + ProductAttributeInterface::CODE_TIER_PRICE, + $meta, + null, + 'children' + ); + if ($tierPricePath) { + $pricePath = $this->arrayManager->findPath( + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE, + $meta, + $tierPricePath + ); + + if ($pricePath) { + $priceMeta = $this->arrayManager->get($pricePath, $meta); + $updatedStructure = $this->getUpdatedTierPriceStructure($priceMeta); + $meta = $this->arrayManager->remove($pricePath, $meta); + $meta = $this->arrayManager->merge( + $this->arrayManager->slicePath($pricePath, 0, -1), + $meta, + $updatedStructure + ); + } + } + return $meta; + } + + /** + * Get updated tier price structure. + * + * @param array $priceMeta + * @return array + */ + private function getUpdatedTierPriceStructure(array $priceMeta) + { + $priceTypeOptions = $this->productPriceOptions->toOptionArray(); + $firstOption = $priceTypeOptions ? current($priceTypeOptions) : null; + + $priceMeta['arguments']['data']['config']['visible'] = $firstOption + && $firstOption['value'] == ProductPriceOptionsInterface::VALUE_FIXED; + $priceMeta['arguments']['data']['config']['validation'] = [ + 'validate-number' => true, + ]; + return [ + 'price_value' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'componentType' => Container::NAME, + 'formElement' => Container::NAME, + 'dataType' => Price::NAME, + 'component' => 'Magento_Ui/js/form/components/group', + 'label' => __('Price'), + 'enableLabel' => true, + 'dataScope' => '', + 'additionalClasses' => 'control-grouped', + 'sortOrder' => isset($priceMeta['arguments']['data']['config']['sortOrder']) + ? $priceMeta['arguments']['data']['config']['sortOrder'] : 40, + ], + ], + ], + 'children' => [ + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_VALUE_TYPE => [ + 'arguments' => [ + 'data' => [ + 'options' => $priceTypeOptions, + 'config' => [ + 'componentType' => Field::NAME, + 'formElement' => Select::NAME, + 'dataType' => 'text', + 'component' => 'Magento_Catalog/js/tier-price/value-type-select', + 'prices' => [ + ProductPriceOptionsInterface::VALUE_FIXED => '${ $.parentName }.' + . ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE, + ProductPriceOptionsInterface::VALUE_PERCENT => '${ $.parentName }.' + . ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PERCENTAGE_VALUE, + ], + ], + ], + ], + ], + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE => $priceMeta, + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PERCENTAGE_VALUE => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'componentType' => Field::NAME, + 'formElement' => Input::NAME, + 'dataType' => Price::NAME, + 'addbefore' => '%', + 'validation' => [ + 'validate-number' => true, + 'less-than-equals-to' => 100 + ], + 'visible' => $firstOption + && $firstOption['value'] == ProductPriceOptionsInterface::VALUE_PERCENT, + ], + ], + ], + ], + 'price_calc' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'componentType' => Container::NAME, + 'component' => 'Magento_Catalog/js/tier-price/percentage-processor', + 'visible' => false + ], + ], + ] + ] + ], + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php index b8f13d899be65..6329c338e7c8d 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index 9993d4657533a..304739b957220 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -1,7 +1,7 @@ @@ -73,7 +73,6 @@ - @@ -143,6 +142,10 @@ Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Attributes 120 + + Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\TierPrice + 150 + @@ -166,4 +169,14 @@ product_form.product_form + + + Magento\Catalog\Model\Config\Source\Product\Options\TierPrice + + + + + Magento\Catalog\Model\Attribute\ScopeOverriddenValue + + diff --git a/app/code/Magento/Catalog/etc/adminhtml/events.xml b/app/code/Magento/Catalog/etc/adminhtml/events.xml index a77e1e741a4be..034204feff5c9 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/events.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/adminhtml/menu.xml b/app/code/Magento/Catalog/etc/adminhtml/menu.xml index d0f15c930e6d2..ee0d1ec5c4117 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/menu.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/menu.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/adminhtml/routes.xml b/app/code/Magento/Catalog/etc/adminhtml/routes.xml index 8dac88c2a22cd..5deeddb3bb4bd 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/routes.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/routes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 85949a953fc53..c5a1b3686fbe5 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/catalog_attributes.xml b/app/code/Magento/Catalog/etc/catalog_attributes.xml index d822d36eabfee..650652aa94555 100644 --- a/app/code/Magento/Catalog/etc/catalog_attributes.xml +++ b/app/code/Magento/Catalog/etc/catalog_attributes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/catalog_attributes.xsd b/app/code/Magento/Catalog/etc/catalog_attributes.xsd index 00384d783eff1..d95d5a17c258e 100644 --- a/app/code/Magento/Catalog/etc/catalog_attributes.xsd +++ b/app/code/Magento/Catalog/etc/catalog_attributes.xsd @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index a86b005be2857..4a8a523e0d55c 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/crontab.xml b/app/code/Magento/Catalog/etc/crontab.xml index 2288ed4ebd8a8..d69ac8f319b5e 100644 --- a/app/code/Magento/Catalog/etc/crontab.xml +++ b/app/code/Magento/Catalog/etc/crontab.xml @@ -1,7 +1,7 @@ @@ -13,5 +13,8 @@ 0 0 * * * + + * * * * * + diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 49ad7d67d7058..ea568c6937ebf 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -1,7 +1,7 @@ @@ -47,6 +47,21 @@ + + + + + + + + + + + + + + + @@ -142,6 +157,11 @@ 9,15,30 + + + 1000 + + 5,10,15,20,25 @@ -833,4 +853,62 @@ Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor + + + + + + + + Magento\Catalog\Model\ResourceModel\Product\StatusBaseSelectProcessor + + + + + + + Magento\Catalog\Model\ResourceModel\Product\Website\SelectProcessor + + + + + + Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor + + + + + + simple + virtual + + + + + + + simple + virtual + bundle + + + + + + + simple + virtual + bundle + + + + + + + simple + virtual + + + + diff --git a/app/code/Magento/Catalog/etc/eav_attributes.xml b/app/code/Magento/Catalog/etc/eav_attributes.xml index c480ea4dd2322..005402937232f 100644 --- a/app/code/Magento/Catalog/etc/eav_attributes.xml +++ b/app/code/Magento/Catalog/etc/eav_attributes.xml @@ -1,7 +1,7 @@ @@ -30,6 +30,7 @@ + diff --git a/app/code/Magento/Catalog/etc/events.xml b/app/code/Magento/Catalog/etc/events.xml index 544abf6b9e069..237382bfa387d 100644 --- a/app/code/Magento/Catalog/etc/events.xml +++ b/app/code/Magento/Catalog/etc/events.xml @@ -1,7 +1,7 @@ @@ -51,4 +51,10 @@ + + + + + + diff --git a/app/code/Magento/Catalog/etc/extension_attributes.xml b/app/code/Magento/Catalog/etc/extension_attributes.xml index 509c3240bb6c8..3136b3df67274 100644 --- a/app/code/Magento/Catalog/etc/extension_attributes.xml +++ b/app/code/Magento/Catalog/etc/extension_attributes.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 9b99bd4a78140..ca1e1e244f49c 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -1,7 +1,7 @@ @@ -18,9 +18,6 @@ Magento\Catalog\Model\ResourceModel\Category\Collection\FetchStrategy - - - true diff --git a/app/code/Magento/Catalog/etc/frontend/events.xml b/app/code/Magento/Catalog/etc/frontend/events.xml index 5ef8c72468314..dd225750f73be 100644 --- a/app/code/Magento/Catalog/etc/frontend/events.xml +++ b/app/code/Magento/Catalog/etc/frontend/events.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/frontend/page_types.xml b/app/code/Magento/Catalog/etc/frontend/page_types.xml index 2557d79a1a49b..8f929046afeef 100644 --- a/app/code/Magento/Catalog/etc/frontend/page_types.xml +++ b/app/code/Magento/Catalog/etc/frontend/page_types.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/frontend/routes.xml b/app/code/Magento/Catalog/etc/frontend/routes.xml index 5adaf604c51d5..d4d52559673d6 100644 --- a/app/code/Magento/Catalog/etc/frontend/routes.xml +++ b/app/code/Magento/Catalog/etc/frontend/routes.xml @@ -1,7 +1,7 @@ @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/Catalog/etc/frontend/sections.xml b/app/code/Magento/Catalog/etc/frontend/sections.xml index 7c36594283640..0bc9c63494b33 100644 --- a/app/code/Magento/Catalog/etc/frontend/sections.xml +++ b/app/code/Magento/Catalog/etc/frontend/sections.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/indexer.xml b/app/code/Magento/Catalog/etc/indexer.xml index 88e6da345c393..5c2ca91e525d9 100644 --- a/app/code/Magento/Catalog/etc/indexer.xml +++ b/app/code/Magento/Catalog/etc/indexer.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/module.xml b/app/code/Magento/Catalog/etc/module.xml index 1250b55b96848..571176694547a 100644 --- a/app/code/Magento/Catalog/etc/module.xml +++ b/app/code/Magento/Catalog/etc/module.xml @@ -1,12 +1,12 @@ - + diff --git a/app/code/Magento/Catalog/etc/mview.xml b/app/code/Magento/Catalog/etc/mview.xml index d6614e837dde3..4600bb7fad370 100644 --- a/app/code/Magento/Catalog/etc/mview.xml +++ b/app/code/Magento/Catalog/etc/mview.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/product_options.xml b/app/code/Magento/Catalog/etc/product_options.xml index 48d62d0c2c0ad..43bf4865cb49e 100644 --- a/app/code/Magento/Catalog/etc/product_options.xml +++ b/app/code/Magento/Catalog/etc/product_options.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/product_options.xsd b/app/code/Magento/Catalog/etc/product_options.xsd index a6bcb74ac2894..18b5934c1410f 100644 --- a/app/code/Magento/Catalog/etc/product_options.xsd +++ b/app/code/Magento/Catalog/etc/product_options.xsd @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/product_options_merged.xsd b/app/code/Magento/Catalog/etc/product_options_merged.xsd index 2a5951c57787d..7b9e6fa2650ec 100644 --- a/app/code/Magento/Catalog/etc/product_options_merged.xsd +++ b/app/code/Magento/Catalog/etc/product_options_merged.xsd @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/product_types.xml b/app/code/Magento/Catalog/etc/product_types.xml index a1516fee38ed5..513f0905b13ce 100644 --- a/app/code/Magento/Catalog/etc/product_types.xml +++ b/app/code/Magento/Catalog/etc/product_types.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/product_types.xsd b/app/code/Magento/Catalog/etc/product_types.xsd index e0cb33802851e..06999fbeddc7a 100644 --- a/app/code/Magento/Catalog/etc/product_types.xsd +++ b/app/code/Magento/Catalog/etc/product_types.xsd @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/product_types_base.xsd b/app/code/Magento/Catalog/etc/product_types_base.xsd index 94d8b87d167e3..eddd7a6845488 100644 --- a/app/code/Magento/Catalog/etc/product_types_base.xsd +++ b/app/code/Magento/Catalog/etc/product_types_base.xsd @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/product_types_merged.xsd b/app/code/Magento/Catalog/etc/product_types_merged.xsd index 1a1a9bfd8214c..1b1d92c163989 100644 --- a/app/code/Magento/Catalog/etc/product_types_merged.xsd +++ b/app/code/Magento/Catalog/etc/product_types_merged.xsd @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/view.xml b/app/code/Magento/Catalog/etc/view.xml index 756888e3b688f..8c7500d9c1374 100644 --- a/app/code/Magento/Catalog/etc/view.xml +++ b/app/code/Magento/Catalog/etc/view.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/webapi.xml b/app/code/Magento/Catalog/etc/webapi.xml index 99670c347a89b..1101a7bc93814 100644 --- a/app/code/Magento/Catalog/etc/webapi.xml +++ b/app/code/Magento/Catalog/etc/webapi.xml @@ -1,7 +1,7 @@ @@ -246,6 +246,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/etc/webapi_rest/di.xml b/app/code/Magento/Catalog/etc/webapi_rest/di.xml index 8606cd8de1136..67e74dfbfd44e 100644 --- a/app/code/Magento/Catalog/etc/webapi_rest/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_rest/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/webapi_soap/di.xml b/app/code/Magento/Catalog/etc/webapi_soap/di.xml index 9d2f4abfa5b46..cb5273e4aeac5 100644 --- a/app/code/Magento/Catalog/etc/webapi_soap/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_soap/di.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/etc/widget.xml b/app/code/Magento/Catalog/etc/widget.xml index e9907e5b17700..f54d4af816c09 100644 --- a/app/code/Magento/Catalog/etc/widget.xml +++ b/app/code/Magento/Catalog/etc/widget.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index 2ad2194b36466..94b32f979383d 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -183,7 +183,7 @@ Categories,Categories "Category is not available for requested store.","Category is not available for requested store." "There was a category move error.","There was a category move error." "There was a category move error. %1","There was a category move error. %1" -"You moved the category","You moved the category" +"You moved the category.","You moved the category." "Attribute ""%1"" is required.","Attribute ""%1"" is required." "You saved the category.","You saved the category." "Something went wrong while saving the category.","Something went wrong while saving the category." diff --git a/app/code/Magento/Catalog/registration.php b/app/code/Magento/Catalog/registration.php index 96b9df94d399e..fada27f08c173 100644 --- a/app/code/Magento/Catalog/registration.php +++ b/app/code/Magento/Catalog/registration.php @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/CATALOG_PRODUCT_COMPOSITE_CONFIGURE_ERROR.xml b/app/code/Magento/Catalog/view/adminhtml/layout/CATALOG_PRODUCT_COMPOSITE_CONFIGURE_ERROR.xml index 2809386e4f94e..30add348f7d2b 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/CATALOG_PRODUCT_COMPOSITE_CONFIGURE_ERROR.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/CATALOG_PRODUCT_COMPOSITE_CONFIGURE_ERROR.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/CATALOG_PRODUCT_COMPOSITE_UPDATE_RESULT.xml b/app/code/Magento/Catalog/view/adminhtml/layout/CATALOG_PRODUCT_COMPOSITE_UPDATE_RESULT.xml index a71dd55d3dfc0..ec97c79610237 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/CATALOG_PRODUCT_COMPOSITE_UPDATE_RESULT.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/CATALOG_PRODUCT_COMPOSITE_UPDATE_RESULT.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_add.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_add.xml index 5f376149e96bf..d9c70ae487903 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_add.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_add.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_create.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_create.xml index 0a8b4e2509cd0..02734a674189e 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_create.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_create.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_edit.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_edit.xml index 1f975b4a701a3..799c50dfc4756 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_edit.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_category_edit.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_action_attribute_edit.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_action_attribute_edit.xml index be147a4270879..3a073f75eef12 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_action_attribute_edit.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_action_attribute_edit.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_alertspricegrid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_alertspricegrid.xml index 39b7f82b2e78d..0cd56d138149e 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_alertspricegrid.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_alertspricegrid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_alertsstockgrid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_alertsstockgrid.xml index 74e558c34cc79..d098da96d3e52 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_alertsstockgrid.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_alertsstockgrid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit.xml index ad6d9f5d4ef94..ddf02a7cdb9c2 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit_form.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit_form.xml index 59c5d5cff3158..8f4780d34b17d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit_form.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit_popup.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit_popup.xml index 5049f5ac8e3f5..a19d29a98720e 100755 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit_popup.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_attribute_edit_popup.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_change_attribute_set.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_change_attribute_set.xml index 82007f774971f..422cf537c3081 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_change_attribute_set.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_change_attribute_set.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssell.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssell.xml index 6f95ce72a20bf..9c1280a2500df 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssell.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssell.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssellgrid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssellgrid.xml index 808e95a885616..96c66485f2132 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssellgrid.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssellgrid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_customoptions.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_customoptions.xml index 064463bd0d613..39781cc8adcf0 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_customoptions.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_customoptions.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_edit.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_edit.xml index d8b0ce7c957e0..3375f5b8233f5 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_edit.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_edit.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_form.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_form.xml index bdcd5da65bbea..194c745e6a65a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_form.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_grid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_grid.xml index 7343969a40ba4..e214ccad3dc21 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_grid.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_grid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_index.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_index.xml index 7ee21e218051f..bad6a5d165535 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_index.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_new.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_new.xml index 0d6dae0c99a87..cb993bc892eac 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_new.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_new.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_options.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_options.xml index e52d7e7ec1a14..7d88ff2a04384 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_options.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_options.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_optionsimportgrid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_optionsimportgrid.xml index 699b6084c314a..7f6f62943bbea 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_optionsimportgrid.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_optionsimportgrid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_related.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_related.xml index e1f2eb0403580..6b688eeec2084 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_related.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_related.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_relatedgrid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_relatedgrid.xml index 8d2fb11dc18c1..4a306dd725b91 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_relatedgrid.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_relatedgrid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_reload.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_reload.xml index 82007f774971f..422cf537c3081 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_reload.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_reload.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_block.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_block.xml index 1dc0de6498cb1..bbbc1e21669e1 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_block.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_block.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_edit.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_edit.xml index d61a2c344d666..7caf391119166 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_edit.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_edit.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_index.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_index.xml index 23b859d526e10..b25eecbbc2502 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_index.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_index.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsell.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsell.xml index f7ec295f154a0..ce0b1521d82e6 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsell.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsell.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsellgrid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsellgrid.xml index 097649c8c0aa3..83c19659b5135 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsellgrid.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsellgrid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/requirejs-config.js b/app/code/Magento/Catalog/view/adminhtml/requirejs-config.js index d2e45cbfb42ee..848d1f1da908c 100644 --- a/app/code/Magento/Catalog/view/adminhtml/requirejs-config.js +++ b/app/code/Magento/Catalog/view/adminhtml/requirejs-config.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -17,4 +17,4 @@ var config = { deps: [ 'Magento_Catalog/catalog/product' ] -}; \ No newline at end of file +}; diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml index b1f8197650fd9..eaeed5b12ebaf 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml @@ -1,6 +1,6 @@
    -
    - - - - getStores() as $_store): ?> - - - - - - - getLabelValues() ?> - getStores() as $_store): ?> - - - - -
    getName() ?>
    - getReadOnly()):?> disabled="disabled"/> -
    +
    +
    + + + + getStores() as $_store): ?> + + + + + + + getLabelValues() ?> + getStores() as $_store): ?> + + + + +
    getName() ?>
    + getReadOnly()):?> disabled="disabled"/> +
    +
    diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml index 500126c24b530..1f1dc0925f2c0 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml @@ -1,6 +1,6 @@ getStoresSortedBySortOrder(); ?> -
    +
    escapeHtml(__('Manage Options (Values of Your Attribute)')); ?> - -
    +
    +
    diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml index 1c58e3424b1c1..d73e4c6095d9d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml @@ -1,6 +1,6 @@ escapeJs(__('Please select a node.')) ?>' + }); + return; + } + for (i in currentNode.childNodes) { if (currentNode.childNodes[i].id) { child = editSet.currentNode.childNodes[i]; @@ -253,31 +264,40 @@ addGroup : function() { prompt({ + title: "", content: "", value: "", + validation: true, + validationRules: ['required-entry'], + attributesForm: { + novalidate: 'novalidate', + action: '' + }, + attributesField: { + name: 'name', + 'data-validate': '{required:true}', + maxlength: '255' + }, actions: { confirm: function (group_name) { group_name = group_name.strip(); - if( group_name == '' ) { - self.addGroup(); - } else if( group_name != false && group_name != null && group_name != '' ) { - - if (!editSet.validateGroupName(group_name, 0)) { - return; - } - - var newNode = new Ext.tree.TreeNode({ - text : group_name.escapeHTML(), - cls : 'folder', - allowDrop : true, - allowDrag : true - }); - TreePanels.root.appendChild(newNode); - newNode.addListener('beforemove', editSet.groupBeforeMove); - newNode.addListener('beforeinsert', editSet.groupBeforeInsert); - newNode.addListener('beforeappend', editSet.groupBeforeInsert); - newNode.addListener('click', editSet.register); + + if (!editSet.validateGroupName(group_name, 0)) { + return; } + + var newNode = new Ext.tree.TreeNode({ + text : group_name.escapeHTML(), + cls : 'folder', + allowDrop : true, + allowDrag : true + }); + + TreePanels.root.appendChild(newNode); + newNode.addListener('beforemove', editSet.groupBeforeMove); + newNode.addListener('beforeinsert', editSet.groupBeforeInsert); + newNode.addListener('beforeappend', editSet.groupBeforeInsert); + newNode.addListener('click', editSet.register); } } }); @@ -302,7 +322,7 @@ } for (var i=0; i < TreePanels.root.childNodes.length; i++) { if (TreePanels.root.childNodes[i].text.toLowerCase() == name.toLowerCase() && TreePanels.root.childNodes[i].id != exceptNodeId) { - errorText = ''; + errorText = ''; alert({ content: errorText.replace("/name/",name) }); diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml index 526d2f98ead9c..2928eff384df6 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml @@ -1,5 +1,5 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/add.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/add.phtml index 536e840249b78..cf40ee6d78af6 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/add.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/add.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/main.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/main.phtml index 3b5748da54823..2cdb9f451a86f 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/main.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/main.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml index 0a2fcb84c996c..767b54e0fdadf 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml index 5d35a9e8be152..cb60254d4d8c7 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml @@ -1,6 +1,6 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/wysiwyg/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/wysiwyg/js.phtml index 5ffacc462ac83..82d2111b986cd 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/wysiwyg/js.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/wysiwyg/js.phtml @@ -1,6 +1,6 @@ @@ -375,19 +375,6 @@ categoryURL Key10 - - ${ $.provider }:data.use_default.url_key - - - - - - - - Use Default - boolean - checkbox - 20 @@ -453,10 +440,9 @@ - admin__field-no-label + admin__field-x-small 170 - - Use Parent Category Settings + Use Parent Category Settings boolean checkbox @@ -464,6 +450,8 @@ 0 0 + Magento_Ui/js/form/element/single-checkbox + toggle @@ -509,20 +497,21 @@ - admin__field-no-label + admin__field-x-small 210 - - Apply Design to Products + Apply Design to Products boolean checkbox - - ns = ${ $.ns }, index = custom_use_parent_settings :checked - 1 0 0 + Magento_Ui/js/form/element/single-checkbox + toggle + + ns = ${ $.ns }, index = custom_use_parent_settings:checked + diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/crosssell_product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/crosssell_product_listing.xml index 965694d7f94c4..c562cf1096fa3 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/crosssell_product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/crosssell_product_listing.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml index 9852ad74121c8..28522ca5c2ba2 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/new_category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/new_category_form.xml index dbe6aa9eb7d91..a0307886770c6 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/new_category_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/new_category_form.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml index 870304c881627..ab5ab6e288e13 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attributes_grid.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attributes_grid.xml index 4067cd062de6c..b4b625dbdac92 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attributes_grid.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attributes_grid.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_custom_options_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_custom_options_listing.xml index 5c7292637129d..2b6db7050d0a9 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_custom_options_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_custom_options_listing.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_form.xml index 2db3d337822b6..b99e01147c00d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_form.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml index ec88952af5033..c2aa0b8c69622 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml @@ -1,7 +1,7 @@ @@ -23,6 +23,9 @@ Magento_Ui/js/grid/provider + + filters.store_id + diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/related_product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/related_product_listing.xml index 25350158c5297..55863e0a21c0d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/related_product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/related_product_listing.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/upsell_product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/upsell_product_listing.xml index fc03f128f4617..26703ba03c9a0 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/upsell_product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/upsell_product_listing.xml @@ -1,7 +1,7 @@ diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/apply-to-type-switcher.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/apply-to-type-switcher.js index 595638d8ca6ef..13543ade8f726 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/apply-to-type-switcher.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/apply-to-type-switcher.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ define([ diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/base-image-uploader.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/base-image-uploader.js index 9c0d986c7e9f2..75cdd9ecf66fd 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/base-image-uploader.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/base-image-uploader.js @@ -1,7 +1,8 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + /*global alert:true*/ define([ 'jquery', @@ -20,8 +21,10 @@ define([ * @protected */ options: { - maxImageUploadCount : 10 + maxImageUploadCount: 10 }, + + /** @inheritdoc */ _create: function () { var $container = this.element, imageTmpl = mageTemplate(this.element.find('[data-template=image]').html()), @@ -30,22 +33,31 @@ define([ mainClass = 'base-image', maximumImageCount = 5, $fieldCheckBox = $container.closest('[data-attribute-code=image]').find(':checkbox'), - isDefaultChecked = $fieldCheckBox.is(':checked'); + isDefaultChecked = $fieldCheckBox.is(':checked'), + findElement, updateVisibility; if (isDefaultChecked) { $fieldCheckBox.trigger('click'); } - var findElement = function (data) { + /** + * @param {Object} data + * @return {HTMLElement} + */ + findElement = function (data) { return $container.find('.image:not(.image-placeholder)').filter(function () { if (!$(this).data('image')) { return false; } + return $(this).data('image').file === data.file; }).first(); }; - var updateVisibility = function () { + + /** Update image visibility. */ + updateVisibility = function () { var elementsList = $container.find('.image:not(.removed-item)'); + elementsList.each(function (index) { $(this)[index < maximumImageCount ? 'show' : 'hide'](); }); @@ -78,12 +90,13 @@ define([ }); $galleryContainer.on('moveElement', function (event, data) { - var $element = findElement(data.imageData); + var $element = findElement(data.imageData), + $after; if (data.position === 0) { $container.prepend($element); } else { - var $after = $container.find('.image').eq(data.position); + $after = $container.find('.image').eq(data.position); if (!$element.is($after)) { $element.insertAfter($after); @@ -93,8 +106,10 @@ define([ }); $container.on('click', '[data-role=make-base-button]', function (event) { + var data; + event.preventDefault(); - var data = $(event.target).closest('.image').data('image'); + data = $(event.target).closest('.image').data('image'); $galleryContainer.productGallery('setBase', data); }); @@ -108,6 +123,11 @@ define([ items: '.image:not(.image-placeholder)', distance: 8, tolerance: 'pointer', + + /** + * @param {jQuery.Event} event + * @param {Object} data + */ stop: function (event, data) { $galleryContainer.trigger('setPosition', { imageData: data.item.data('image'), @@ -122,6 +142,11 @@ define([ dropZone: $dropPlaceholder.closest('[data-attribute-code]'), acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, maxFileSize: this.element.data('maxFileSize'), + + /** + * @param {jQuery.Event} event + * @param {Object} data + */ done: function (event, data) { $dropPlaceholder.find('.progress-bar').text('').removeClass('in-progress'); @@ -137,13 +162,22 @@ define([ }); } }, - change: function(e, data) { + + /** + * @param {jQuery.Event} e + * @param {Object} data + */ + change: function (e, data) { if (data.files.length > this.options.maxImageUploadCount) { $('body').notification('clear').notification('add', { error: true, - message: $.mage.__('You can\'t upload more than ' + this.options.maxImageUploadCount - + ' images in one time'), - insertMethod: function(message) { + message: $.mage.__('You can\'t upload more than ' + this.options.maxImageUploadCount + + ' images in one time'), + + /** + * @param {*} message + */ + insertMethod: function (message) { $('.page-main-actions').after(message); } }); @@ -151,20 +185,39 @@ define([ return false; } }.bind(this), + + /** + * @param {jQuery.Event} event + * @param {*} data + */ add: function (event, data) { $(this).fileupload('process', data).done(function () { data.submit(); }); }, + + /** + * @param {jQuery.Event} e + * @param {Object} data + */ progress: function (e, data) { var progress = parseInt(data.loaded / data.total * 100, 10); + $dropPlaceholder.find('.progress-bar').addClass('in-progress').text(progress + '%'); }, + + /** + * @param {jQuery.Event} event + */ start: function (event) { var uploaderContainer = $(event.target).closest('.image-placeholder'); uploaderContainer.addClass('loading'); }, + + /** + * @param {jQuery.Event} event + */ stop: function (event) { var uploaderContainer = $(event.target).closest('.image-placeholder'); diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js index ad5f52095f36b..1da9c2c379c37 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/edit.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/edit.js index 28bc0734ef033..8c5e5bf1ca966 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/edit.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/edit.js @@ -1,78 +1,86 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ /** * Create/edit some category */ + +/* global tree */ define([ 'jquery', 'prototype' ], function (jQuery) { + 'use strict'; + + /** Category submit. */ + var categorySubmit = function () { + var activeTab = $('active_tab_id'), + params = {}, + fields, i,categoryId, isCreating, path, parentId, currentNode, oldClass, newClass; - var categorySubmit = function (url, useAjax) { - var activeTab = $('active_tab_id'); - if (activeTab) { - if (activeTab.tabsJsObject && activeTab.tabsJsObject.tabs('activeAnchor')) { - activeTab.value = activeTab.tabsJsObject.tabs('activeAnchor').prop('id'); + if (activeTab) { + if (activeTab.tabsJsObject && activeTab.tabsJsObject.tabs('activeAnchor')) { + activeTab.value = activeTab.tabsJsObject.tabs('activeAnchor').prop('id'); + } } - } - var params = {}; - var fields = $('category_edit_form').getElementsBySelector('input', 'select'); - for (var i=0; i').modal({ - title: $.mage.__('New Attribute'), + title: $.mage.__('New Attribute'), type: 'slide', buttons: [], + + /** @inheritdoc */ opened: function () { $(this).parent().addClass('modal-content-new-attribute'); self.iframe = $('', + 'Tag is removed SomeObject', ], [ 'Tag is removed SomeScript', @@ -96,6 +97,7 @@ public function filterDataProvider() 'Tag is removed SomeLink', 'Tag is removed SomeFrame', 'Tag is removed SomeIFrame', + 'Tag is removed SomeObject', ], ], 'Base64' => [ diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/InputTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/InputTest.php index 1f1d2fb18fdc5..0d83b43e4f3bc 100644 --- a/lib/internal/Magento/Framework/Filter/Test/Unit/InputTest.php +++ b/lib/internal/Magento/Framework/Filter/Test/Unit/InputTest.php @@ -1,6 +1,6 @@ json = $json ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); + $this->serialize = $serialize ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Serialize::class); + parent::__construct( + $context, + $registry, + $resource, + $resourceCollection, + $data + ); + } + /** * Init resource model * Set flag_code if it is specified in arguments @@ -68,9 +113,13 @@ public function beforeSave() public function getFlagData() { if ($this->hasFlagData()) { - return unserialize($this->getData('flag_data')); - } else { - return null; + $flagData = $this->getData('flag_data'); + $data = $this->json->unserialize($flagData); + if (JSON_ERROR_NONE == json_last_error()) { + return $data; + } else { + return $this->serialize->unserialize($flagData); + } } } @@ -82,7 +131,7 @@ public function getFlagData() */ public function setFlagData($value) { - return $this->setData('flag_data', serialize($value)); + return $this->setData('flag_data', $this->json->serialize($value)); } /** diff --git a/lib/internal/Magento/Framework/Flag/FlagResource.php b/lib/internal/Magento/Framework/Flag/FlagResource.php index 0b94b37a2fc2a..fe12e7e85d600 100644 --- a/lib/internal/Magento/Framework/Flag/FlagResource.php +++ b/lib/internal/Magento/Framework/Flag/FlagResource.php @@ -1,6 +1,6 @@ (CURLPROTO_HTTP + | CURLPROTO_HTTPS + | CURLPROTO_FTP + | CURLPROTO_FTPS + ), + 'verifypeer' => true, + 'verifyhost' => 2 + ]; /** * Curl handle @@ -41,7 +49,11 @@ class Curl implements \Zend_Http_Client_Adapter_Interface 'ssl_cert' => CURLOPT_SSLCERT, 'userpwd' => CURLOPT_USERPWD, 'useragent' => CURLOPT_USERAGENT, - 'referer' => CURLOPT_REFERER + 'referer' => CURLOPT_REFERER, + 'protocols' => CURLOPT_PROTOCOLS, + 'verifypeer' => CURLOPT_SSL_VERIFYPEER, + 'verifyhost' => CURLOPT_SSL_VERIFYHOST, + 'sslversion' => CURLOPT_SSLVERSION, ]; /** @@ -55,8 +67,6 @@ class Curl implements \Zend_Http_Client_Adapter_Interface * Apply current configuration array to transport resource * * @return \Magento\Framework\HTTP\Adapter\Curl - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function _applyConfig() { @@ -65,22 +75,28 @@ protected function _applyConfig() curl_setopt($this->_getResource(), $option, $value); } - if (empty($this->_config)) { - return $this; + // apply config options + foreach ($this->getDefaultConfig() as $option => $value) { + curl_setopt($this->_getResource(), $option, $value); } - $verifyPeer = isset($this->_config['verifypeer']) ? $this->_config['verifypeer'] : true; - curl_setopt($this->_getResource(), CURLOPT_SSL_VERIFYPEER, $verifyPeer); - - $verifyHost = isset($this->_config['verifyhost']) ? $this->_config['verifyhost'] : 2; - curl_setopt($this->_getResource(), CURLOPT_SSL_VERIFYHOST, $verifyHost); + return $this; + } - foreach ($this->_config as $param => $curlOption) { + /** + * Get default options + * + * @return array + */ + private function getDefaultConfig() + { + $config = []; + foreach (array_keys($this->_config) as $param) { if (array_key_exists($param, $this->_allowedParams)) { - curl_setopt($this->_getResource(), $this->_allowedParams[$param], $this->_config[$param]); + $config[$this->_allowedParams[$param]] = $this->_config[$param]; } } - return $this; + return $config; } /** @@ -116,7 +132,9 @@ public function addOption($option, $value) */ public function setConfig($config = []) { - $this->_config = $config; + foreach ($config as $key => $value) { + $this->_config[$key] = $value; + } return $this; } @@ -160,6 +178,9 @@ public function write($method, $url, $http_ver = '1.1', $headers = [], $body = ' curl_setopt($this->_getResource(), CURLOPT_POSTFIELDS, $body); } elseif ($method == \Zend_Http_Client::GET) { curl_setopt($this->_getResource(), CURLOPT_HTTPGET, true); + } elseif ($method == \Zend_Http_Client::PUT) { + curl_setopt($this->_getResource(), CURLOPT_CUSTOMREQUEST, \Zend_Http_Client::PUT); + curl_setopt($this->_getResource(), CURLOPT_POSTFIELDS, $body); } if (is_array($headers)) { @@ -268,6 +289,13 @@ public function multiRequest($urls, $options = []) $multihandle = curl_multi_init(); + // add default parameters + foreach ($this->getDefaultConfig() as $defaultOption => $defaultValue) { + if (!isset($options[$defaultOption])) { + $options[$defaultOption] = $defaultValue; + } + } + foreach ($urls as $key => $url) { $handles[$key] = curl_init(); curl_setopt($handles[$key], CURLOPT_URL, $url); diff --git a/lib/internal/Magento/Framework/HTTP/Adapter/FileTransferFactory.php b/lib/internal/Magento/Framework/HTTP/Adapter/FileTransferFactory.php index 32a5478a45f60..2fb852910cb87 100644 --- a/lib/internal/Magento/Framework/HTTP/Adapter/FileTransferFactory.php +++ b/lib/internal/Magento/Framework/HTTP/Adapter/FileTransferFactory.php @@ -1,6 +1,6 @@ + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Curl implements \Magento\Framework\HTTP\ClientInterface { @@ -16,7 +17,7 @@ class Curl implements \Magento\Framework\HTTP\ClientInterface * Max supported protocol by curl CURL_SSLVERSION_TLSv1_2 * @var int */ - private static $sslVersion = 6; + private $sslVersion; /** * Hostname @@ -86,7 +87,7 @@ class Curl implements \Magento\Framework\HTTP\ClientInterface /** * Curl - * @var object + * @var resource */ protected $_ch; @@ -117,10 +118,11 @@ public function setTimeout($value) } /** - * Constructor + * @param int|null $sslVersion */ - public function __construct() + public function __construct($sslVersion = null) { + $this->sslVersion = $sslVersion; } /** @@ -228,8 +230,11 @@ public function get($uri) /** * Make POST request * + * String type was added to parameter $param in order to support sending JSON or XML requests. + * This feature was added base on Community Pull Request https://github.com/magento/magento2/pull/8373 + * * @param string $uri - * @param array $params + * @param array|string $params * @return void * * @see \Magento\Framework\HTTP\Client#post($uri, $params) @@ -333,9 +338,13 @@ public function getStatus() /** * Make request + * + * String type was added to parameter $param in order to support sending JSON or XML requests. + * This feature was added base on Community Pull Request https://github.com/magento/magento2/pull/8373 + * * @param string $method * @param string $uri - * @param array $params + * @param array|string $params - use $params as a string in case of JSON or XML POST request. * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -346,7 +355,7 @@ protected function makeRequest($method, $uri, $params = []) $this->curlOption(CURLOPT_URL, $uri); if ($method == 'POST') { $this->curlOption(CURLOPT_POST, 1); - $this->curlOption(CURLOPT_POSTFIELDS, http_build_query($params)); + $this->curlOption(CURLOPT_POSTFIELDS, is_array($params) ? http_build_query($params) : $params); } elseif ($method == "GET") { $this->curlOption(CURLOPT_HTTPGET, 1); } else { @@ -377,10 +386,11 @@ protected function makeRequest($method, $uri, $params = []) $this->curlOption(CURLOPT_PORT, $this->_port); } - //$this->curlOption(CURLOPT_HEADER, 1); $this->curlOption(CURLOPT_RETURNTRANSFER, 1); $this->curlOption(CURLOPT_HEADERFUNCTION, [$this, 'parseHeaders']); - $this->curlOption(CURLOPT_SSLVERSION, self::$sslVersion); + if ($this->sslVersion !== null) { + $this->curlOption(CURLOPT_SSLVERSION, $this->sslVersion); + } if (count($this->_curlUserOptions)) { foreach ($this->_curlUserOptions as $k => $v) { @@ -415,6 +425,7 @@ public function doError($string) * @param resource $ch curl handle, not needed * @param string $data * @return int + * @throws \Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function parseHeaders($ch, $data) @@ -422,11 +433,10 @@ protected function parseHeaders($ch, $data) if ($this->_headerCount == 0) { $line = explode(" ", trim($data), 3); if (count($line) != 3) { - return $this->doError("Invalid response line returned from server: " . $data); + $this->doError("Invalid response line returned from server: " . $data); } $this->_responseStatus = intval($line[1]); } else { - //var_dump($data); $name = $value = ''; $out = explode(": ", trim($data), 2); if (count($out) == 2) { diff --git a/lib/internal/Magento/Framework/HTTP/Client/Socket.php b/lib/internal/Magento/Framework/HTTP/Client/Socket.php index 2f6a34b06700b..75cdb61c793b3 100644 --- a/lib/internal/Magento/Framework/HTTP/Client/Socket.php +++ b/lib/internal/Magento/Framework/HTTP/Client/Socket.php @@ -1,6 +1,6 @@ _saveAlpha($newImage); + } + // fill new image with required color $this->_fillBackgroundColor($newImage); diff --git a/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php b/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php index ffdde07768022..63fe81f41cc2a 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php +++ b/lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php @@ -1,6 +1,6 @@ compositeImage( + $this->_imageHandler, + \Imagick::COMPOSITE_COPYOPACITY, + $dims['dst']['x'], + $dims['dst']['y'] + ); + $newImage->compositeImage( $this->_imageHandler, \Imagick::COMPOSITE_OVER, diff --git a/lib/internal/Magento/Framework/Image/AdapterFactory.php b/lib/internal/Magento/Framework/Image/AdapterFactory.php index e1eee4dfdbbe4..c18180814a28b 100644 --- a/lib/internal/Magento/Framework/Image/AdapterFactory.php +++ b/lib/internal/Magento/Framework/Image/AdapterFactory.php @@ -1,6 +1,6 @@ ['type' => Table::TYPE_TEXT, 'size' => 255], @@ -71,11 +75,13 @@ class Base implements ActionInterface /** * @var array + * @deprecated */ protected $filterColumns; /** * @var array + * @deprecated */ protected $searchColumns; @@ -96,6 +102,7 @@ class Base implements ActionInterface /** * @var String + * @deprecated */ protected $string; @@ -106,11 +113,13 @@ class Base implements ActionInterface /** * @var array + * @deprecated */ protected $filterable = []; /** * @var array + * @deprecated */ protected $searchable = []; @@ -272,6 +281,7 @@ protected function getPrimaryFieldset() protected function createResultCollection() { $select = $this->getPrimaryResource()->getSelect(); + $select->reset(\Magento\Framework\DB\Select::COLUMNS); $select->columns($this->getPrimaryResource()->getIdFieldName()); foreach ($this->data['fieldsets'] as $fieldset) { if (isset($fieldset['references'])) { @@ -342,6 +352,8 @@ protected function prepareFields() * * @param array $field * @return void + * + * @deprecated */ protected function saveFieldByType($field) { diff --git a/lib/internal/Magento/Framework/Indexer/Action/Dummy.php b/lib/internal/Magento/Framework/Indexer/Action/Dummy.php index e0531b31bd61a..fe079b9f89794 100644 --- a/lib/internal/Magento/Framework/Indexer/Action/Dummy.php +++ b/lib/internal/Magento/Framework/Indexer/Action/Dummy.php @@ -1,6 +1,6 @@ createResultCollection() - : $this->createResultCollection()->addFieldToFilter($this->getPrimaryResource()->getRowIdFieldName(), $ids); + : $this->createResultCollection()->addFieldToFilter($this->getPrimaryResource()->getIdFieldName(), $ids); } } diff --git a/lib/internal/Magento/Framework/Indexer/ActionFactory.php b/lib/internal/Magento/Framework/Indexer/ActionFactory.php index dbd7794244ae4..cf67c059b7dcc 100644 --- a/lib/internal/Magento/Framework/Indexer/ActionFactory.php +++ b/lib/internal/Magento/Framework/Indexer/ActionFactory.php @@ -1,6 +1,6 @@ nodeName) { case 'title': - $data['title'] = $this->getTranslatedNodeValue($childNode); + $data['title'] = $childNode->nodeValue; break; case 'description': - $data['description'] = $this->getTranslatedNodeValue($childNode); + $data['description'] = $childNode->nodeValue; break; case 'saveHandler': $data['saveHandler'] = $this->getAttributeValue($childNode, 'class'); @@ -207,6 +207,7 @@ protected function convertField(\DOMElement $node, $data) * * @param \DOMNode $node * @return string + * @deprecated */ protected function getTranslatedNodeValue(\DOMNode $node) { diff --git a/lib/internal/Magento/Framework/Indexer/Config/Reader.php b/lib/internal/Magento/Framework/Indexer/Config/Reader.php index 1179c2df98d49..a3ee733d783ca 100644 --- a/lib/internal/Magento/Framework/Indexer/Config/Reader.php +++ b/lib/internal/Magento/Framework/Indexer/Config/Reader.php @@ -1,6 +1,6 @@ $documentValue) { $batch[$documentName] = $documentValue; - if (++$i >= $size) { - $items[] = $batch; + if (++$i == $size) { + yield $batch; $i = 0; $batch = []; } } if (count($batch) > 0) { - $items[] = $batch; + yield $batch; } - return $items; } } diff --git a/lib/internal/Magento/Framework/Indexer/SaveHandler/Grid.php b/lib/internal/Magento/Framework/Indexer/SaveHandler/Grid.php index ddce47a9b21d6..e183c2946662d 100644 --- a/lib/internal/Magento/Framework/Indexer/SaveHandler/Grid.php +++ b/lib/internal/Magento/Framework/Indexer/SaveHandler/Grid.php @@ -1,6 +1,6 @@ getName() . $dimension->getValue(); } } + return $this->resource->getTableName(implode('_', $tableNameParts)); } @@ -63,10 +64,12 @@ public function resolve($index, array $dimensions) */ private function getScopeId($dimension) { - if (is_numeric($dimension->getValue())) { - return $dimension->getValue(); - } else { - return $this->scopeResolver->getScope($dimension->getValue())->getId(); + $scopeId = $dimension->getValue(); + + if (!is_numeric($scopeId)) { + $scopeId = $this->scopeResolver->getScope($scopeId)->getId(); } + + return $scopeId; } } diff --git a/lib/internal/Magento/Framework/Indexer/StateInterface.php b/lib/internal/Magento/Framework/Indexer/StateInterface.php index 28c8bb52911d2..7f501aed3bc50 100644 --- a/lib/internal/Magento/Framework/Indexer/StateInterface.php +++ b/lib/internal/Magento/Framework/Indexer/StateInterface.php @@ -1,6 +1,6 @@ assertSame($expected, $this->object->getItems($items, $size)); + $data = $this->object->getItems($items, $size); + $this->assertSame($expected, iterator_to_array($data)); } /** diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/Config/ConverterTest.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/Config/ConverterTest.php index be8591c3032bd..8072e17f7de70 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/Config/ConverterTest.php +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/Config/ConverterTest.php @@ -1,6 +1,6 @@ @@ -18,4 +18,4 @@ Indexer public name three Indexer public description three - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_merged_two.xml b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_merged_two.xml index fc3b526f5f6a2..4e6cccae3fe2b 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_merged_two.xml +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_merged_two.xml @@ -1,7 +1,7 @@ @@ -14,4 +14,4 @@ Indexer public name three Indexer public description three - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_one.xml b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_one.xml index 68b7f9a6ad3f8..7c48afc403d54 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_one.xml +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_one.xml @@ -1,7 +1,7 @@ @@ -10,4 +10,4 @@ Indexer public name one Indexer public description one - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_three.xml b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_three.xml index fc3b526f5f6a2..4e6cccae3fe2b 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_three.xml +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_three.xml @@ -1,7 +1,7 @@ @@ -14,4 +14,4 @@ Indexer public name three Indexer public description three - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_two.xml b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_two.xml index f1c73990be463..e92913cd191fc 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_two.xml +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/indexer_two.xml @@ -1,7 +1,7 @@ @@ -14,4 +14,4 @@ Indexer public name three Indexer public description three - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/invalidIndexerXmlArray.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/invalidIndexerXmlArray.php index c7352fdf98455..eb98346f3e88f 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/invalidIndexerXmlArray.php +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/_files/invalidIndexerXmlArray.php @@ -1,6 +1,6 @@ @@ -14,4 +14,4 @@ Indexer public name Indexer public description - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/Indexer/etc/indexer.xsd b/lib/internal/Magento/Framework/Indexer/etc/indexer.xsd index e4a33c72fe883..f196cc1f32a2d 100644 --- a/lib/internal/Magento/Framework/Indexer/etc/indexer.xsd +++ b/lib/internal/Magento/Framework/Indexer/etc/indexer.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Indexer/etc/indexer_merged.xsd b/lib/internal/Magento/Framework/Indexer/etc/indexer_merged.xsd index 91887a4e4ddcb..054cbaf141b0c 100644 --- a/lib/internal/Magento/Framework/Indexer/etc/indexer_merged.xsd +++ b/lib/internal/Magento/Framework/Indexer/etc/indexer_merged.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php index 0c920717bdf38..701532fdd04ad 100644 --- a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php +++ b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php @@ -1,6 +1,6 @@ _omConfig = $omConfig; $this->_relations = $relations; @@ -95,10 +107,11 @@ public function __construct( $this->_cacheId = $cacheId; $this->_reader = $reader; $this->_scopeList = $scopeList; - + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(Serialize::class); $intercepted = $this->_cache->load($this->_cacheId); if ($intercepted !== false) { - $this->_intercepted = unserialize($intercepted); + $this->_intercepted = $this->serializer->unserialize($intercepted); } else { $this->initialize($this->_classDefinitions->getClasses()); } @@ -129,7 +142,7 @@ public function initialize($classDefinitions = []) foreach ($classDefinitions as $class) { $this->hasPlugins($class); } - $this->_cache->save(serialize($this->_intercepted), $this->_cacheId); + $this->_cache->save($this->serializer->serialize($this->_intercepted), $this->_cacheId); } /** diff --git a/lib/internal/Magento/Framework/Interception/ConfigInterface.php b/lib/internal/Magento/Framework/Interception/ConfigInterface.php index d3e6e4daa2ffe..e6af6296440f3 100644 --- a/lib/internal/Magento/Framework/Interception/ConfigInterface.php +++ b/lib/internal/Magento/Framework/Interception/ConfigInterface.php @@ -2,7 +2,7 @@ /** * Interception config. Tells whether plugins have been added for type. * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Interception; diff --git a/lib/internal/Magento/Framework/Interception/Definition/Compiled.php b/lib/internal/Magento/Framework/Interception/Definition/Compiled.php deleted file mode 100644 index 6fbe9c99dce86..0000000000000 --- a/lib/internal/Magento/Framework/Interception/Definition/Compiled.php +++ /dev/null @@ -1,39 +0,0 @@ -_definitions = $definitions; - } - - /** - * Retrieve list of methods - * - * @param string $type - * @return string[] - */ - public function getMethodList($type) - { - return $this->_definitions[$type]; - } -} diff --git a/lib/internal/Magento/Framework/Interception/Definition/Runtime.php b/lib/internal/Magento/Framework/Interception/Definition/Runtime.php index 6da06b63bed24..b3d5fa8d04412 100644 --- a/lib/internal/Magento/Framework/Interception/Definition/Runtime.php +++ b/lib/internal/Magento/Framework/Interception/Definition/Runtime.php @@ -3,7 +3,7 @@ * \Reflection based plugin method list. Uses reflection to retrieve list of interception methods defined in plugin. * Should be only used in development mode, because it reads method list on every request which is expensive. * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Interception\Definition; diff --git a/lib/internal/Magento/Framework/Interception/DefinitionInterface.php b/lib/internal/Magento/Framework/Interception/DefinitionInterface.php index 72941ba8282fa..f9fd3e2c15f57 100644 --- a/lib/internal/Magento/Framework/Interception/DefinitionInterface.php +++ b/lib/internal/Magento/Framework/Interception/DefinitionInterface.php @@ -2,7 +2,7 @@ /** * Plugin method definitions. Provide the list of interception methods in specified plugin. * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Interception; diff --git a/lib/internal/Magento/Framework/Interception/Interceptor.php b/lib/internal/Magento/Framework/Interception/Interceptor.php index 9aa00e1c75175..359d8902af941 100644 --- a/lib/internal/Magento/Framework/Interception/Interceptor.php +++ b/lib/internal/Magento/Framework/Interception/Interceptor.php @@ -1,6 +1,6 @@ serializer = $serializer ?: $objectManager->get(Serialize::class); + parent::__construct($reader, $configScope, $cache, $cacheId, $this->serializer); $this->_omConfig = $omConfig; $this->_relations = $relations; $this->_definitions = $definitions; @@ -149,6 +166,7 @@ protected function _inheritPlugins($type) } $this->_inherited[$type] = null; if (is_array($plugins) && count($plugins)) { + $this->filterPlugins($plugins); uasort($plugins, [$this, '_sort']); $this->trimInstanceStartingBackslash($plugins); $this->_inherited[$type] = $plugins; @@ -269,9 +287,9 @@ protected function _loadScopedData() $cacheId = implode('|', $this->_scopePriorityScheme) . "|" . $this->_cacheId; $data = $this->_cache->load($cacheId); if ($data) { - list($this->_data, $this->_inherited, $this->_processed) = unserialize($data); - foreach ($this->_scopePriorityScheme as $scope) { - $this->_loadedScopes[$scope] = true; + list($this->_data, $this->_inherited, $this->_processed) = $this->serializer->unserialize($data); + foreach ($this->_scopePriorityScheme as $scopeCode) { + $this->_loadedScopes[$scopeCode] = true; } } else { $virtualTypes = []; @@ -279,18 +297,17 @@ protected function _loadScopedData() if (false == isset($this->_loadedScopes[$scopeCode])) { $data = $this->_reader->read($scopeCode); unset($data['preferences']); - if (!count($data)) { - continue; - } - $this->_inherited = []; - $this->_processed = []; - $this->merge($data); - $this->_loadedScopes[$scopeCode] = true; - foreach ($data as $class => $config) { - if (isset($config['type'])) { - $virtualTypes[] = $class; + if (count($data) > 0) { + $this->_inherited = []; + $this->_processed = []; + $this->merge($data); + foreach ($data as $class => $config) { + if (isset($config['type'])) { + $virtualTypes[] = $class; + } } } + $this->_loadedScopes[$scopeCode] = true; } if ($this->isCurrentScope($scopeCode)) { break; @@ -302,7 +319,10 @@ protected function _loadScopedData() foreach ($this->getClassDefinitions() as $class) { $this->_inheritPlugins($class); } - $this->_cache->save(serialize([$this->_data, $this->_inherited, $this->_processed]), $cacheId); + $this->_cache->save( + $this->serializer->serialize([$this->_data, $this->_inherited, $this->_processed]), + $cacheId + ); } $this->_pluginInstances = []; } @@ -348,4 +368,34 @@ public function merge(array $config) } } } + + /** + * Remove from list not existing plugins + * + * @param array $plugins + * @return void + */ + private function filterPlugins(array &$plugins) + { + foreach ($plugins as $name => $plugin) { + if (empty($plugin['instance'])) { + unset($plugins[$name]); + $this->getLogger()->info("Reference to undeclared plugin with name '{$name}'."); + } + } + } + + /** + * Get logger + * + * @return \Psr\Log\LoggerInterface + * @deprecated + */ + private function getLogger() + { + if ($this->logger === null) { + $this->logger = $this->_objectManager->get(\Psr\Log\LoggerInterface::class); + } + return $this->logger; + } } diff --git a/lib/internal/Magento/Framework/Interception/PluginListInterface.php b/lib/internal/Magento/Framework/Interception/PluginListInterface.php index eeb8735bf74ea..64b8549893885 100644 --- a/lib/internal/Magento/Framework/Interception/PluginListInterface.php +++ b/lib/internal/Magento/Framework/Interception/PluginListInterface.php @@ -2,7 +2,7 @@ /** * List of plugins configured in application * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Interception; diff --git a/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/InterceptorTest.php b/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/InterceptorTest.php index 4c26b41a1547a..43ff5985ec383 100644 --- a/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/InterceptorTest.php +++ b/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/InterceptorTest.php @@ -1,6 +1,6 @@ relationsMock = $this->getMockForAbstractClass( \Magento\Framework\ObjectManager\RelationsInterface::class ); + $this->serializerMock = $this->getMock(SerializerInterface::class); + $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); } /** @@ -131,14 +141,22 @@ public function testHasPluginsWhenDataIsNotCached($expectedResult, $type, $entit $this->relationsMock->expects($this->any())->method('has')->will($this->returnValue($expectedResult)); $this->relationsMock->expects($this->any())->method('getParents')->will($this->returnValue($entityParents)); - $model = new \Magento\Framework\Interception\Config\Config( - $this->readerMock, - $this->configScopeMock, - $this->cacheMock, - $this->relationsMock, - $this->omConfigMock, - $this->definitionMock, - 'interception' + $this->serializerMock->expects($this->once()) + ->method('serialize'); + + $this->serializerMock->expects($this->never())->method('unserialize'); + + $model = $this->objectManagerHelper->getObject( + \Magento\Framework\Interception\Config\Config::class, + [ + 'reader' => $this->readerMock, + 'scopeList' => $this->configScopeMock, + 'cache' => $this->cacheMock, + 'relations' => $this->relationsMock, + 'omConfig' => $this->omConfigMock, + 'classDefinitions' => $this->definitionMock, + 'serializer' => $this->serializerMock, + ] ); $this->assertEquals($expectedResult, $model->hasPlugins($type)); @@ -163,18 +181,32 @@ public function testHasPluginsWhenDataIsCached($expectedResult, $type) ]; $this->readerMock->expects($this->never())->method('read'); $this->cacheMock->expects($this->never())->method('save'); + $serializedValue = 'serializedData'; $this->cacheMock->expects($this->any()) ->method('load') ->with($cacheId) - ->will($this->returnValue(serialize($interceptionData))); - $model = new \Magento\Framework\Interception\Config\Config( - $this->readerMock, - $this->configScopeMock, - $this->cacheMock, - new \Magento\Framework\ObjectManager\Relations\Runtime(), - $this->omConfigMock, - $this->definitionMock, - $cacheId + ->will($this->returnValue($serializedValue)); + + $this->serializerMock->expects($this->never())->method('serialize'); + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->with($serializedValue) + ->willReturn($interceptionData); + + $model = $this->objectManagerHelper->getObject( + \Magento\Framework\Interception\Config\Config::class, + [ + 'reader' => $this->readerMock, + 'scopeList' => $this->configScopeMock, + 'cache' => $this->cacheMock, + 'relations' => $this->objectManagerHelper->getObject( + \Magento\Framework\ObjectManager\Relations\Runtime::class + ), + 'omConfig' => $this->omConfigMock, + 'classDefinitions' => $this->definitionMock, + 'cacheId' => $cacheId, + 'serializer' => $this->serializerMock, + ] ); $this->assertEquals($expectedResult, $model->hasPlugins($type)); diff --git a/lib/internal/Magento/Framework/Interception/Test/Unit/Custom/Module/Model/InterfaceValidator/Item.php b/lib/internal/Magento/Framework/Interception/Test/Unit/Custom/Module/Model/InterfaceValidator/Item.php index 31a811611fbc5..256c495de378c 100644 --- a/lib/internal/Magento/Framework/Interception/Test/Unit/Custom/Module/Model/InterfaceValidator/Item.php +++ b/lib/internal/Magento/Framework/Interception/Test/Unit/Custom/Module/Model/InterfaceValidator/Item.php @@ -1,6 +1,6 @@ 'definitions']; - - /** - * @covers \Magento\Framework\Interception\Definition\Compiled::getMethodList - * @covers \Magento\Framework\Interception\Definition\Compiled::__construct - */ - public function testGetMethodList() - { - $model = new \Magento\Framework\Interception\Definition\Compiled($this->_definitions); - $this->assertEquals('definitions', $model->getMethodList('type')); - } -} diff --git a/lib/internal/Magento/Framework/Interception/Test/Unit/ObjectManager/Config/DeveloperTest.php b/lib/internal/Magento/Framework/Interception/Test/Unit/ObjectManager/Config/DeveloperTest.php index 45a125252c733..3f84e0e30ff99 100644 --- a/lib/internal/Magento/Framework/Interception/Test/Unit/ObjectManager/Config/DeveloperTest.php +++ b/lib/internal/Magento/Framework/Interception/Test/Unit/ObjectManager/Config/DeveloperTest.php @@ -1,6 +1,6 @@ getMock(\Magento\Framework\ObjectManager\Config\Reader\Dom::class, [], [], '', false); $readerMock->expects($this->any())->method('read')->will($this->returnValueMap($readerMap)); - $this->_configScopeMock = $this->getMock(\Magento\Framework\Config\ScopeInterface::class); - $this->_cacheMock = $this->getMock(\Magento\Framework\Config\CacheInterface::class); + $this->configScopeMock = $this->getMock(\Magento\Framework\Config\ScopeInterface::class); + $this->cacheMock = $this->getMock(\Magento\Framework\Config\CacheInterface::class); // turn cache off - $this->_cacheMock->expects($this->any()) + $this->cacheMock->expects($this->any()) ->method('get') ->will($this->returnValue(false)); @@ -59,62 +72,76 @@ protected function setUp() $omConfigMock->expects($this->any())->method('getOriginalInstanceType')->will($this->returnArgument(0)); - $this->_objectManagerMock = $this->getMock(\Magento\Framework\ObjectManagerInterface::class); - $this->_objectManagerMock->expects($this->any())->method('get')->will($this->returnArgument(0)); + $this->objectManagerMock = $this->getMock(ObjectManagerInterface::class); + $this->objectManagerMock->expects($this->any()) + ->method('get') + ->willReturnArgument(0); + $this->serializerMock = $this->getMock(SerializerInterface::class); $definitions = new \Magento\Framework\ObjectManager\Definition\Runtime(); - $this->_model = new \Magento\Framework\Interception\PluginList\PluginList( - $readerMock, - $this->_configScopeMock, - $this->_cacheMock, - new \Magento\Framework\ObjectManager\Relations\Runtime(), - $omConfigMock, - new \Magento\Framework\Interception\Definition\Runtime(), - $this->_objectManagerMock, - $definitions, - ['global'], - 'interception' + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->object = $objectManagerHelper->getObject( + \Magento\Framework\Interception\PluginList\PluginList::class, + [ + 'reader' => $readerMock, + 'configScope' => $this->configScopeMock, + 'cache' => $this->cacheMock, + 'relations' => new \Magento\Framework\ObjectManager\Relations\Runtime(), + 'omConfig' => $omConfigMock, + 'definitions' => new \Magento\Framework\Interception\Definition\Runtime(), + 'objectManager' => $this->objectManagerMock, + 'classDefinitions' => $definitions, + 'scopePriorityScheme' => ['global'], + 'cacheId' => 'interception', + 'serializer' => $this->serializerMock + ] + ); + + $this->loggerMock = $this->getMock(\Psr\Log\LoggerInterface::class); + $objectManagerHelper->setBackwardCompatibleProperty( + $this->object, + 'logger', + $this->loggerMock ); } public function testGetPlugin() { - $this->_configScopeMock->expects($this->any())->method('getCurrentScope')->will($this->returnValue('backend')); - $this->_model->getNext(\Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, 'getName'); - $this->_model->getNext( + $this->configScopeMock->expects($this->any())->method('getCurrentScope')->will($this->returnValue('backend')); + $this->object->getNext(\Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, 'getName'); + $this->object->getNext( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer::class, 'getName' ); - $this->_model->getNext( + $this->object->getNext( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\StartingBackslash::class, 'getName' ); - $this->assertEquals( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemPlugin\Simple::class, - $this->_model->getPlugin( + $this->object->getPlugin( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, 'simple_plugin' ) ); $this->assertEquals( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemPlugin\Advanced::class, - $this->_model->getPlugin( + $this->object->getPlugin( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, 'advanced_plugin' ) ); $this->assertEquals( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainerPlugin\Simple::class, - $this->_model->getPlugin( + $this->object->getPlugin( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer::class, 'simple_plugin' ) ); $this->assertEquals( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\StartingBackslash\Plugin::class, - $this->_model->getPlugin( + $this->object->getPlugin( \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\StartingBackslash::class, 'simple_plugin' ) @@ -131,14 +158,14 @@ public function testGetPlugin() */ public function testGetPlugins($expectedResult, $type, $method, $scopeCode, $code = '__self') { - $this->_configScopeMock->expects( + $this->configScopeMock->expects( $this->any() )->method( 'getCurrentScope' )->will( $this->returnValue($scopeCode) ); - $this->assertEquals($expectedResult, $this->_model->getNext($type, $method, $code)); + $this->assertEquals($expectedResult, $this->object->getNext($type, $method, $code)); } /** @@ -206,11 +233,42 @@ public function getPluginsDataProvider() */ public function testInheritPluginsWithNonExistingClass() { - $this->_configScopeMock->expects($this->any()) + $this->configScopeMock->expects($this->any()) ->method('getCurrentScope') ->will($this->returnValue('frontend')); - $this->_model->getNext('SomeType', 'someMethod'); + $this->object->getNext('SomeType', 'someMethod'); + } + + public function testLoadScopedDataNotCached() + { + $this->configScopeMock->expects($this->exactly(3)) + ->method('getCurrentScope') + ->will($this->returnValue('scope')); + $this->serializerMock->expects($this->once()) + ->method('serialize'); + $this->serializerMock->expects($this->never()) + ->method('unserialize'); + $this->cacheMock->expects($this->once()) + ->method('save'); + + $this->assertEquals(null, $this->object->getNext('Type', 'method')); + } + + /** + * @covers \Magento\Framework\Interception\PluginList\PluginList::getNext + * @covers \Magento\Framework\Interception\PluginList\PluginList::_inheritPlugins + */ + public function testInheritPluginsWithNotExistingPlugin() + { + $this->loggerMock->expects($this->once()) + ->method('info') + ->with("Reference to undeclared plugin with name 'simple_plugin'."); + $this->configScopeMock->expects($this->any()) + ->method('getCurrentScope') + ->will($this->returnValue('frontend')); + + $this->assertNull($this->object->getNext('typeWithoutInstance', 'someMethod')); } /** @@ -219,17 +277,52 @@ public function testInheritPluginsWithNonExistingClass() */ public function testLoadScopedDataCached() { - $this->_configScopeMock->expects($this->once()) + $this->configScopeMock->expects($this->once()) ->method('getCurrentScope') ->will($this->returnValue('scope')); $data = [['key'], ['key'], ['key']]; + $serializedData = 'serialized data'; - $this->_cacheMock->expects($this->once()) + $this->serializerMock->expects($this->never()) + ->method('serialize'); + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->willReturn($data); + $this->cacheMock->expects($this->once()) ->method('load') ->with('global|scope|interception') - ->will($this->returnValue(serialize($data))); + ->willReturn($serializedData); + + $this->assertEquals(null, $this->object->getNext('Type', 'method')); + } + + /** + * @covers \Magento\Framework\Interception\PluginList\PluginList::getNext + * @covers \Magento\Framework\Interception\PluginList\PluginList::_loadScopedData + */ + public function testLoadScopeDataWithEmptyData() + { + $this->objectManagerMock->expects($this->any()) + ->method('get') + ->will($this->returnArgument(0)); + $this->configScopeMock->expects($this->any()) + ->method('getCurrentScope') + ->will($this->returnValue('emptyscope')); - $this->assertEquals(null, $this->_model->getNext('Type', 'method')); + $this->assertEquals( + [4 => ['simple_plugin']], + $this->object->getNext( + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, + 'getName' + ) + ); + $this->assertEquals( + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemPlugin\Simple::class, + $this->object->getPlugin( + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, + 'simple_plugin' + ) + ); } } diff --git a/lib/internal/Magento/Framework/Interception/Test/Unit/_files/reader_mock_map.php b/lib/internal/Magento/Framework/Interception/Test/Unit/_files/reader_mock_map.php index 832a5a67599da..faca7a24bfff8 100644 --- a/lib/internal/Magento/Framework/Interception/Test/Unit/_files/reader_mock_map.php +++ b/lib/internal/Magento/Framework/Interception/Test/Unit/_files/reader_mock_map.php @@ -1,6 +1,6 @@ 'NonExistingPluginClass', ], ], + ], + 'typeWithoutInstance' => [ + 'plugins' => [ + 'simple_plugin' => [], + ], ] ] + ], + [ + 'emptyscope', + [ + + ] ] ]; diff --git a/lib/internal/Magento/Framework/Intl/DateTimeFactory.php b/lib/internal/Magento/Framework/Intl/DateTimeFactory.php index 6d45f9503d005..87d27b1d085ed 100644 --- a/lib/internal/Magento/Framework/Intl/DateTimeFactory.php +++ b/lib/internal/Magento/Framework/Intl/DateTimeFactory.php @@ -1,6 +1,6 @@ _data[self::CUSTOM_ATTRIBUTES][$key]) + ? $this->_data[self::CUSTOM_ATTRIBUTES][$key] + : null; if ($data instanceof \Magento\Framework\Api\AttributeValue) { $data = $data->getValue(); } + if (null !== $index && isset($data[$index])) { + return $data[$index]; + } } } + return $data; } diff --git a/lib/internal/Magento/Framework/Model/AbstractModel.php b/lib/internal/Magento/Framework/Model/AbstractModel.php index dbbb20021b9f4..67db6a10c40db 100644 --- a/lib/internal/Magento/Framework/Model/AbstractModel.php +++ b/lib/internal/Magento/Framework/Model/AbstractModel.php @@ -1,6 +1,6 @@ _beforeLoad($modelId, $field); $this->_getResource()->load($this, $modelId, $field); - $this->_afterLoad(); - $this->setOrigData(); - $this->_hasDataChanges = false; - $this->updateStoredData(); return $this; } @@ -577,6 +572,18 @@ protected function _afterLoad() return $this; } + /** + * Process operation before object load + * + * @param string $identifier + * @param string|null $field + * @return void + */ + public function beforeLoad($identifier, $field = null) + { + $this->_beforeLoad($identifier, $field); + } + /** * Object after load processing. Implemented as public interface for supporting objects after load in collections * @@ -584,7 +591,6 @@ protected function _afterLoad() */ public function afterLoad() { - $this->getResource()->afterLoad($this); $this->_afterLoad(); $this->updateStoredData(); return $this; diff --git a/lib/internal/Magento/Framework/Model/ActionValidator/RemoveAction.php b/lib/internal/Magento/Framework/Model/ActionValidator/RemoveAction.php index 2e207fde345e7..bbc9abe22efc1 100644 --- a/lib/internal/Magento/Framework/Model/ActionValidator/RemoveAction.php +++ b/lib/internal/Magento/Framework/Model/ActionValidator/RemoveAction.php @@ -2,7 +2,7 @@ /** * Action validator, remove action * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/lib/internal/Magento/Framework/Model/ActionValidator/RemoveAction/Allowed.php b/lib/internal/Magento/Framework/Model/ActionValidator/RemoveAction/Allowed.php index a956f99217e6e..dee1df394e2ea 100644 --- a/lib/internal/Magento/Framework/Model/ActionValidator/RemoveAction/Allowed.php +++ b/lib/internal/Magento/Framework/Model/ActionValidator/RemoveAction/Allowed.php @@ -2,7 +2,7 @@ /** * Action validator for remove action * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Model\ActionValidator\RemoveAction; diff --git a/lib/internal/Magento/Framework/Model/CallbackPool.php b/lib/internal/Magento/Framework/Model/CallbackPool.php index cf4fee33e276b..12b3e05927ac7 100644 --- a/lib/internal/Magento/Framework/Model/CallbackPool.php +++ b/lib/internal/Magento/Framework/Model/CallbackPool.php @@ -1,6 +1,6 @@ unsetData($field); } else { - $object->setData($field, serialize($value ?: $defaultValue)); + $object->setData($field, $this->getSerializer()->serialize($value ?: $defaultValue)); } return $this; @@ -132,13 +139,7 @@ protected function _serializeField(DataObject $object, $field, $defaultValue = n */ protected function _unserializeField(DataObject $object, $field, $defaultValue = null) { - $value = $object->getData($field); - - if ($value) { - $unserializedValue = @unserialize($value); - $value = $unserializedValue !== false || $value === 'b:0;' ? $unserializedValue : $value; - } - + $value = $this->getSerializer()->unserialize($object->getData($field)); if (empty($value)) { $object->setData($field, $defaultValue); } else { @@ -228,4 +229,18 @@ protected function _getColumnsForEntityLoad(\Magento\Framework\Model\AbstractMod } return $columns; } + + /** + * Get serializer + * + * @return Json + * @deprecated + */ + protected function getSerializer() + { + if (null === $this->serializer) { + $this->serializer = ObjectManager::getInstance()->get(Json::class); + } + return $this->serializer; + } } diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php index fc189473505ab..22ddc6bee445d 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php @@ -1,18 +1,20 @@ transactionManager = $context->getTransactionManager(); $this->_resources = $context->getResources(); $this->objectRelationProcessor = $context->getObjectRelationProcessor(); @@ -332,6 +336,7 @@ public function getConnection() */ public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null) { + $object->beforeLoad($value, $field); if ($field === null) { $field = $this->getIdFieldName(); } @@ -348,7 +353,10 @@ public function load(\Magento\Framework\Model\AbstractModel $object, $value, $fi $this->unserializeFields($object); $this->_afterLoad($object); - + $object->afterLoad(); + $object->setOrigData(); + $object->setHasDataChanges(false); + return $this; } @@ -375,6 +383,7 @@ protected function _getLoadSelect($field, $value, $object) * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @throws \Exception + * @throws AlreadyExistsException * @api */ public function save(\Magento\Framework\Model\AbstractModel $object) @@ -409,6 +418,10 @@ public function save(\Magento\Framework\Model\AbstractModel $object) } $this->addCommitCallback([$object, 'afterCommitCallback'])->commit(); $object->setHasDataChanges(false); + } catch (DuplicateException $e) { + $this->rollBack(); + $object->setHasDataChanges(true); + throw new AlreadyExistsException(new Phrase('Unique constraint violation found'), $e); } catch (\Exception $e) { $this->rollBack(); $object->setHasDataChanges(true); diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php index 6c82dafb6c44e..79cd8d4be318c 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/Collection/AbstractCollection.php @@ -1,6 +1,6 @@ _serializeField($dataObject, $field, $defaultValue, $unsetEmpty); + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->serializerMock = $this->getMock(Json::class); + $this->abstractResource = $objectManager->getObject(AbstractResourceStub::class); + $objectManager->setBackwardCompatibleProperty( + $this->abstractResource, + 'serializer', + $this->serializerMock + ); + } - static::assertEquals($expectation, $dataObject->getDataByKey($field)); + /** + * @param array $arguments + * @param string $expected + * @param array|string|int $serializeCalledWith + * @param int $numSerializeCalled + * @dataProvider serializeFieldsDataProvider + */ + public function testSerializeFields( + array $arguments, + $expected, + $serializeCalledWith, + $numSerializeCalled = 1 + ) { + /** @var DataObject $dataObject */ + list($dataObject, $field, $defaultValue, $unsetEmpty) = $arguments; + $this->serializerMock->expects($this->exactly($numSerializeCalled)) + ->method('serialize') + ->with($serializeCalledWith) + ->willReturn($expected); + $this->abstractResource->_serializeField($dataObject, $field, $defaultValue, $unsetEmpty); + $this->assertEquals($expected, $dataObject->getData($field)); } /** * @return array */ - public function serializableFieldsDataProvider() + public function serializeFieldsDataProvider() { + $array = ['a', 'b', 'c']; + $string = 'i am string'; + $integer = 969; + $empty = ''; $dataObject = new DataObject( [ - 'object' => new \stdClass(), - 'array' => ['a', 'b', 'c'], - 'string' => 'i am string', - 'int' => 969, - 'serialized_object' => 'O:8:"stdClass":0:{}', - 'empty_value' => '', - 'empty_value_with_default' => '' + 'array' => $array, + 'string' => $string, + 'integer' => $integer, + 'empty' => $empty, + 'empty_with_default' => '' ] ); - return [ - [[$dataObject, 'object', null, false], serialize($dataObject->getDataByKey('object'))], - [[$dataObject, 'array', null, false], serialize($dataObject->getDataByKey('array'))], - [[$dataObject, 'string', null, false], serialize($dataObject->getDataByKey('string'))], - [[$dataObject, 'int', null, false], serialize($dataObject->getDataByKey('int'))], [ - [$dataObject, 'serialized_object', null, false], - serialize($dataObject->getDataByKey('serialized_object')) + [$dataObject, 'array', null, false], + '["a","b","c"]', + $array + ], + [ + [$dataObject, 'string', null, false], + '"i am string"', + $string + ], + [ + [$dataObject, 'integer', null, false], + '969', + $integer + ], + [ + [$dataObject, 'empty', null, true], + null, + $empty, + 0 ], - [[$dataObject, 'empty_value', null, true], null], - [[$dataObject, 'empty_value_with_default', new \stdClass(), false], 'O:8:"stdClass":0:{}'], + [ + [$dataObject, 'empty_with_default', 'default', false], + '"default"', + 'default' + ] ]; } /** * @param array $arguments - * @param mixed $expectation - * @dataProvider unserializableFieldsDataProvider + * @param array|string|int|boolean $expected + * @dataProvider unserializeFieldsDataProvider */ - public function testUnserializeFields(array $arguments, $expectation) + public function testUnserializeFields(array $arguments, $expected) { /** @var DataObject $dataObject */ list($dataObject, $field, $defaultValue) = $arguments; - - $abstractResource = new AbstractResourceStub(); - - $abstractResource->_unserializeField($dataObject, $field, $defaultValue); - - static::assertEquals($expectation, $dataObject->getDataByKey($field)); + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->with($dataObject->getData($field)) + ->willReturn($expected); + $this->abstractResource->_unserializeField($dataObject, $field, $defaultValue); + $this->assertEquals($expected, $dataObject->getData($field)); } /** * @return array */ - public function unserializableFieldsDataProvider() + public function unserializeFieldsDataProvider() { $dataObject = new DataObject( [ - 'object' => serialize(new \stdClass()), - 'array' => serialize(['a', 'b', 'c']), - 'string' => serialize('i am string'), - 'int' => serialize(969), - 'serialized_object' => serialize('O:8:"stdClass":0:{}'), - 'empty_value_with_default' => serialize(''), + 'array' => '["a","b","c"]', + 'string' => '"i am string"', + 'integer' => '969', + 'empty_with_default' => '""', 'not_serialized_string' => 'i am string', - 'serialized_boolean_false' => serialize(false) + 'serialized_boolean_false' => 'false' ] ); - - $defaultValue = new \stdClass(); - return [ - [[$dataObject, 'object', null], unserialize($dataObject->getDataByKey('object'))], - [[$dataObject, 'array', null], unserialize($dataObject->getDataByKey('array'))], - [[$dataObject, 'string', null], unserialize($dataObject->getDataByKey('string'))], - [[$dataObject, 'int', null], unserialize($dataObject->getDataByKey('int'))], - [[$dataObject, 'serialized_object', null], unserialize($dataObject->getDataByKey('serialized_object'))], - [[$dataObject, 'empty_value_with_default', $defaultValue], $defaultValue], - [[$dataObject, 'not_serialized_string', null], 'i am string'], - [[$dataObject, 'serialized_boolean_false', null], false] + [ + [$dataObject, 'array', null], + ['a', 'b', 'c'] + ], + [ + [$dataObject, 'string', null], + 'i am string' + ], + [ + [$dataObject, 'integer', null], + 969 + ], + [ + [$dataObject, 'empty_with_default', 'default', false], + 'default' + ], + [ + [$dataObject, 'not_serialized_string', null], + 'i am string' + ], + [ + [$dataObject, 'serialized_boolean_false', null], + false, + ] ]; } @@ -116,32 +174,31 @@ public function testCommitZeroLevel() ->disableOriginalConstructor() ->getMock(); - $abstractResource = new AbstractResourceStub(); - $abstractResource->setConnection($connection); - $abstractResource->addCommitCallback( + $this->abstractResource->setConnection($connection); + $this->abstractResource->addCommitCallback( function () use ($closureExpectation) { $closureExpectation->setData(1); } ); - $abstractResource->addCommitCallback( + $this->abstractResource->addCommitCallback( function () use ($closureExpectation) { $closureExpectation->getData(); } ); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('commit'); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('getTransactionLevel') ->willReturn(0); - $closureExpectation->expects(static::once()) + $closureExpectation->expects($this->once()) ->method('setData') ->with(1); - $closureExpectation->expects(static::once()) + $closureExpectation->expects($this->once()) ->method('getData'); - $abstractResource->commit(); + $this->abstractResource->commit(); } /** @@ -152,23 +209,22 @@ public function testCommitZeroLevelCallbackException() /** @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject $connection */ $connection = $this->getMock(AdapterInterface::class); - $abstractResource = new AbstractResourceStub(); - $abstractResource->setConnection($connection); - $abstractResource->addCommitCallback( + $this->abstractResource->setConnection($connection); + $this->abstractResource->addCommitCallback( function () { throw new \Exception(); } ); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('commit'); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('getTransactionLevel') ->willReturn(0); - $abstractResource->commit(); + $this->abstractResource->commit(); } - + public function testCommitNotCompletedTransaction() { /** @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject $connection */ @@ -178,24 +234,23 @@ public function testCommitNotCompletedTransaction() ->disableOriginalConstructor() ->getMock(); - $abstractResource = new AbstractResourceStub(); - $abstractResource->setConnection($connection); - $abstractResource->addCommitCallback( + $this->abstractResource->setConnection($connection); + $this->abstractResource->addCommitCallback( function () use ($closureExpectation) { $closureExpectation->setData(1); } ); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('commit'); - $connection->expects(static::once()) + $connection->expects($this->once()) ->method('getTransactionLevel') ->willReturn(1); - $closureExpectation->expects(static::never()) + $closureExpectation->expects($this->never()) ->method('setData') ->with(1); - $abstractResource->commit(); + $this->abstractResource->commit(); } } diff --git a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php index 1dcb84d276840..a6f3add797d16 100644 --- a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php +++ b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php @@ -1,12 +1,16 @@ willReturn($this->transactionManagerMock); $this->_model = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [$contextMock], '', true, @@ -116,7 +120,7 @@ public function addUniqueFieldDataProvider() public function testAddUniqueFieldArray() { $this->assertInstanceOf( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, $this->_model->addUniqueField(['someField']) ); } @@ -134,7 +138,7 @@ public function testGetIdFieldname() { $data = 'MainTableName'; $idFieldNameProperty = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, '_idFieldName' + AbstractDb::class, '_idFieldName' ); $idFieldNameProperty->setAccessible(true); $idFieldNameProperty->setValue($this->_model, $data); @@ -158,7 +162,7 @@ public function testGetMainTableException() public function testGetMainTable($tableName, $expectedResult) { $mainTableProperty = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, '_mainTable' ); $mainTableProperty->setAccessible(true); @@ -195,7 +199,7 @@ public function testGetTable() $this->returnValue('tableName') ); $tablesProperty = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, '_tables' ); $tablesProperty->setAccessible(true); @@ -215,7 +219,7 @@ public function testGetChecksumNegative() */ public function testGetChecksum($checksum, $expected) { - $connectionMock = $this->getMock(\Magento\Framework\DB\Adapter\AdapterInterface::class, [], [], '', false); + $connectionMock = $this->getMock(AdapterInterface::class, [], [], '', false); $connectionMock->expects($this->once())->method('getTablesChecksum')->with($checksum)->will( $this->returnValue([$checksum => 'checksum']) ); @@ -242,7 +246,7 @@ public function getChecksumProvider() public function testResetUniqueField() { $uniqueFields = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, '_uniqueFields' ); $uniqueFields->setAccessible(true); @@ -254,7 +258,7 @@ public function testResetUniqueField() public function testGetUniqueFields() { $uniqueFieldsReflection = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, '_uniqueFields' ); $uniqueFieldsReflection->setAccessible(true); @@ -269,36 +273,26 @@ public function testGetValidationRulesBeforeSave() public function testLoad() { - $contextMock = $this->getMock(\Magento\Framework\Model\Context::class, [], [], '', false); - $registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false); - $abstractModelMock = $this->getMockForAbstractClass( - \Magento\Framework\Model\AbstractModel::class, - [$contextMock, $registryMock], - '', - false, - true, - true, - ['__wakeup'] - ); - - $value = 'some_value'; - $idFieldName = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, - '_idFieldName' - ); - $idFieldName->setAccessible(true); - $idFieldName->setValue($this->_model, 'field_value'); - + /** @var \Magento\Framework\Model\AbstractModel|\PHPUnit_Framework_MockObject_MockObject $object */ + $object = $this->getMockBuilder(\Magento\Framework\Model\AbstractModel::class) + ->disableOriginalConstructor() + ->getMock(); + $object->expects($this->once())->method('beforeLoad')->with('some_value', 'field_name'); + $object->expects($this->once())->method('afterLoad')->willReturnSelf(); + $object->expects($this->once())->method('setOrigData')->willReturnSelf(); + $object->expects($this->once())->method('setHasDataChanges')->with(false)->willReturnSelf(); + $result = $this->_model->load($object, 'some_value', 'field_name'); + $this->assertEquals($this->_model, $result); $this->assertInstanceOf( \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, - $this->_model->load($abstractModelMock, $value, $idFieldName) + $result ); } public function testDelete() { $connectionInterfaceMock = $this->getMock( - \Magento\Framework\DB\Adapter\AdapterInterface::class, + AdapterInterface::class, [], [], '', @@ -307,7 +301,7 @@ public function testDelete() $contextMock = $this->getMock(\Magento\Framework\Model\Context::class, [], [], '', false); $registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false); $abstractModelMock = $this->getMockForAbstractClass( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, [$contextMock, $registryMock], '', false, @@ -321,7 +315,7 @@ public function testDelete() ); $abstractModelMock->expects($this->once())->method('getData')->willReturn(['data' => 'value']); - $connectionMock = $this->getMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $connectionMock = $this->getMock(AdapterInterface::class); $this->transactionManagerMock->expects($this->once()) ->method('start') ->with($connectionInterfaceMock) @@ -344,13 +338,13 @@ public function testDelete() $this->returnValue('tableName') ); $mainTableReflection = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, '_mainTable' ); $mainTableReflection->setAccessible(true); $mainTableReflection->setValue($this->_model, 'tableName'); $idFieldNameReflection = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, '_idFieldName' ); $idFieldNameReflection->setAccessible(true); @@ -361,7 +355,7 @@ public function testDelete() $abstractModelMock->expects($this->once())->method('afterDelete'); $abstractModelMock->expects($this->once())->method('afterDeleteCommit'); $this->assertInstanceOf( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, $this->_model->delete($abstractModelMock) ); } @@ -371,7 +365,7 @@ public function testHasDataChangedNegative() $contextMock = $this->getMock(\Magento\Framework\Model\Context::class, [], [], '', false); $registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false); $abstractModelMock = $this->getMockForAbstractClass( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, [$contextMock, $registryMock], '', false, @@ -391,7 +385,7 @@ public function testHasDataChangedNegative() public function testGetDataChanged($getOriginData, $expected) { $connectionInterfaceMock = $this->getMock( - \Magento\Framework\DB\Adapter\AdapterInterface::class, + AdapterInterface::class, [], [], '', @@ -403,7 +397,7 @@ public function testGetDataChanged($getOriginData, $expected) $contextMock = $this->getMock(\Magento\Framework\Model\Context::class, [], [], '', false); $registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false); $abstractModelMock = $this->getMockForAbstractClass( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, [$contextMock, $registryMock], '', false, @@ -412,7 +406,7 @@ public function testGetDataChanged($getOriginData, $expected) ['__wakeup', 'getOrigData', 'getData'] ); $mainTableProperty = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, '_mainTable' ); $mainTableProperty->setAccessible(true); @@ -441,13 +435,13 @@ public function hasDataChangedDataProvider() public function testPrepareDataForUpdate() { - $connectionMock = $this->getMock(\Magento\Framework\DB\Adapter\AdapterInterface::class, [], [], '', false); + $connectionMock = $this->getMock(AdapterInterface::class, [], [], '', false); $context = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( \Magento\Framework\Model\Context::class ); $registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false); $resourceMock = $this->getMock( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [ '_construct', 'getConnection', @@ -459,7 +453,7 @@ public function testPrepareDataForUpdate() false ); $connectionInterfaceMock = $this->getMock( - \Magento\Framework\DB\Adapter\AdapterInterface::class, + AdapterInterface::class, [], [], '', @@ -472,7 +466,7 @@ public function testPrepareDataForUpdate() ->disableOriginalConstructor() ->getMockForAbstractClass(); $abstractModelMock = $this->getMockForAbstractClass( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, [$context, $registryMock, $resourceMock, $resourceCollectionMock] ); $data = 'tableName'; @@ -484,20 +478,19 @@ public function testPrepareDataForUpdate() $this->returnValue('tableName') ); $mainTableReflection = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, '_mainTable' ); $mainTableReflection->setAccessible(true); $mainTableReflection->setValue($this->_model, 'tableName'); $idFieldNameReflection = new \ReflectionProperty( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, '_idFieldName' ); $idFieldNameReflection->setAccessible(true); $idFieldNameReflection->setValue($this->_model, 'idFieldName'); $connectionMock->expects($this->any())->method('save')->with('tableName', 'idFieldName'); $connectionMock->expects($this->any())->method('quoteInto')->will($this->returnValue('idFieldName')); - $abstractModelMock->setIdFieldName('id'); $abstractModelMock->setData( [ @@ -551,7 +544,7 @@ public function testSaveNewObject($pkIncrement) /** * Mock SUT so as not to test extraneous logic */ - $model = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class) + $model = $this->getMockBuilder(AbstractDb::class) ->disableOriginalConstructor() ->setMethods(['_prepareDataForSave', 'getIdFieldName', 'getConnection', 'getMainTable']) ->getMockForAbstractClass(); @@ -568,7 +561,7 @@ public function testSaveNewObject($pkIncrement) $reflectionProperty->setValue($model, $pkIncrement); // Mocked behavior - $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + $connectionMock = $this->getMockBuilder(AdapterInterface::class) ->disableOriginalConstructor() ->setMethods(['lastInsertId']) ->getMockForAbstractClass(); @@ -590,7 +583,7 @@ public function testSaveNewObject($pkIncrement) // Only set object id if not PK autoincrement $setIdInvokedCount = $pkIncrement ? 1 : 0; - $inputObject = $this->getMockBuilder(\Magento\Framework\Model\AbstractModel::class) + $inputObject = $this->getMockBuilder(AbstractModel::class) ->disableOriginalConstructor() ->getMock(); $inputObject->expects($this->exactly($setIdInvokedCount))->method('setId'); @@ -602,9 +595,37 @@ public function testSaveNewObject($pkIncrement) $reflectionMethod->invokeArgs($model, [$inputObject]); } + /** + * @return array + */ public function saveNewObjectDataProvider() { return [[true], [false]]; } + /** + * @expectedException \Magento\Framework\Exception\AlreadyExistsException + */ + public function testDuplicateExceptionProcessingOnSave() + { + $connection = $this->getMock(AdapterInterface::class); + $connection->expects($this->once())->method('rollback'); + + /** @var AbstractDb|\PHPUnit_Framework_MockObject_MockObject $model */ + $model = $this->getMockBuilder(AbstractDb::class) + ->disableOriginalConstructor() + ->setMethods(['getConnection']) + ->getMockForAbstractClass(); + $model->expects($this->any())->method('getConnection')->willReturn($connection); + + /** @var AbstractModel|\PHPUnit_Framework_MockObject_MockObject $object */ + $object = $this->getMockBuilder(AbstractModel::class) + ->disableOriginalConstructor() + ->getMock(); + $object->expects($this->once())->method('hasDataChanges')->willReturn(true); + $object->expects($this->once())->method('beforeSave')->willThrowException(new DuplicateException()); + $object->expects($this->once())->method('setHasDataChanges')->with(true); + + $model->save($object); + } } diff --git a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/Collection/AbstractCollectionTest.php b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/Collection/AbstractCollectionTest.php index d458810732b6b..51fbd2ba00290 100644 --- a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/Collection/AbstractCollectionTest.php +++ b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/Collection/AbstractCollectionTest.php @@ -1,6 +1,6 @@ isEnabled('Vendor_Module'); + * ``` */ -namespace Magento\Framework\Module; - class Manager { /** - * @var Output\ConfigInterface + * The checker of output modules. + * + * @var Output\ConfigInterface the config checker of output modules. + * @deprecated Magento does not support custom disabling/enabling module output since 2.2.0 version. + * The property can be removed in a future major release */ - private $_outputConfig; + private $outputConfig; /** - * @var ModuleListInterface + * The list of all modules. + * + * @var ModuleListInterface the list of all modules. */ - private $_moduleList; + private $moduleList; /** - * @var array + * The list of config paths to ignore. + * + * @var array the list of config paths to ignore. + * @deprecated Magento does not support custom disabling/enabling module output since 2.2.0 version. + * The property can be removed in a future major release */ - private $_outputConfigPaths; + private $outputConfigPaths; /** - * @param Output\ConfigInterface $outputConfig - * @param ModuleListInterface $moduleList - * @param array $outputConfigPaths + * Constructor. + * + * @param Output\ConfigInterface $outputConfig the checker of output modules + * @param ModuleListInterface $moduleList the list of all modules + * @param array $outputConfigPaths the list of config paths to ignore */ public function __construct( Output\ConfigInterface $outputConfig, ModuleListInterface $moduleList, array $outputConfigPaths = [] ) { - $this->_outputConfig = $outputConfig; - $this->_moduleList = $moduleList; - $this->_outputConfigPaths = $outputConfigPaths; + $this->outputConfig = $outputConfig; + $this->moduleList = $moduleList; + $this->outputConfigPaths = $outputConfigPaths; } /** - * Whether a module is enabled in the configuration or not + * Checks whether a module is enabled in the configuration or not. * - * @param string $moduleName Fully-qualified module name - * @return boolean + * @param string $moduleName the fully-qualified module name + * + * @return boolean true if module is enabled, false otherwise */ public function isEnabled($moduleName) { - return $this->_moduleList->has($moduleName); + return $this->moduleList->has($moduleName); } /** - * Whether a module output is permitted by the configuration or not + * Checks whether a module output is permitted by the configuration or not. + * + * @param string $moduleName the fully-qualified module name. * - * @param string $moduleName Fully-qualified module name * @return boolean + * @deprecated Magento does not support custom disabling/enabling module output since 2.2.0 version */ public function isOutputEnabled($moduleName) { - if (!$this->isEnabled($moduleName)) { - return false; - } - if (!$this->_isCustomOutputConfigEnabled($moduleName)) { - return false; - } - if ($this->_outputConfig->isEnabled($moduleName)) { - return false; - } - return true; + return $this->isEnabled($moduleName); } /** - * Whether a configuration switch for a module output permits output or not + * Checks whether a configuration switch for a module output permits output. * * @param string $moduleName Fully-qualified module name + * * @return boolean + * @deprecated Magento does not support custom disabling/enabling module output since 2.2.0 version. + * The method can be removed in a future major release + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function _isCustomOutputConfigEnabled($moduleName) { - if (isset($this->_outputConfigPaths[$moduleName])) { - $configPath = $this->_outputConfigPaths[$moduleName]; - if (defined($configPath)) { - $configPath = constant($configPath); - } - return $this->_outputConfig->isSetFlag($configPath); - } return true; } } diff --git a/lib/internal/Magento/Framework/Module/ModuleList.php b/lib/internal/Magento/Framework/Module/ModuleList.php index caaa5aba3153e..3c1605bed2eb7 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList.php +++ b/lib/internal/Magento/Framework/Module/ModuleList.php @@ -1,6 +1,6 @@ isSetFlag(sprintf(self::XML_PATH_MODULE_OUTPUT_STATUS, $moduleName)); + return false; } /** - * @inheritdoc + * Retrieve module enabled specific path + * + * @param string $path Fully-qualified config path + * @deprecated Magento does not support custom disabling/enabling module output since 2.2.0 version + * @return boolean */ public function isSetFlag($path) { - return $this->_scopeConfig->isSetFlag($path, $this->_storeType); + return false; } } diff --git a/lib/internal/Magento/Framework/Module/Output/ConfigInterface.php b/lib/internal/Magento/Framework/Module/Output/ConfigInterface.php index 2794c424fbe8a..ef55778dfe17c 100644 --- a/lib/internal/Magento/Framework/Module/Output/ConfigInterface.php +++ b/lib/internal/Magento/Framework/Module/Output/ConfigInterface.php @@ -1,16 +1,22 @@ diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/DependencyCheckerTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/DependencyCheckerTest.php index 2c199d5bcf797..301541f4d0f0b 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/DependencyCheckerTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/DependencyCheckerTest.php @@ -1,6 +1,6 @@ _moduleList = $this->getMockForAbstractClass(\Magento\Framework\Module\ModuleListInterface::class); - $this->_moduleList->expects($this->any()) - ->method('getOne') - ->will($this->returnValueMap([ - ['Module_One', ['name' => 'One_Module', 'setup_version' => '1']], - ['Module_Two', ['name' => 'Two_Module', 'setup_version' => '2']], - ['Module_Three', ['name' => 'Two_Three']], - ])); - $this->_outputConfig = $this->getMockForAbstractClass(\Magento\Framework\Module\Output\ConfigInterface::class); - $this->_model = new \Magento\Framework\Module\Manager( - $this->_outputConfig, - $this->_moduleList, - [ - 'Module_Two' => self::XML_PATH_OUTPUT_ENABLED, - ] - ); - } - - public function testIsEnabled() - { - $this->_moduleList->expects($this->exactly(2))->method('has')->will($this->returnValueMap([ - ['Module_Exists', true], - ['Module_NotExists', false], - ])); - $this->assertTrue($this->_model->isEnabled('Module_Exists')); - $this->assertFalse($this->_model->isEnabled('Module_NotExists')); - } - - public function testIsOutputEnabledReturnsFalseForDisabledModule() - { - $this->_outputConfig->expects($this->any())->method('isSetFlag')->will($this->returnValue(true)); - $this->assertFalse($this->_model->isOutputEnabled('Disabled_Module')); - } + private $outputConfig; /** - * @param bool $configValue - * @param bool $expectedResult - * @dataProvider isOutputEnabledGenericConfigPathDataProvider + * @inheritdoc */ - public function testIsOutputEnabledGenericConfigPath($configValue, $expectedResult) + protected function setUp() { - $this->_moduleList->expects($this->once())->method('has')->will($this->returnValue(true)); - $this->_outputConfig->expects($this->once()) - ->method('isEnabled') - ->with('Module_One') - ->will($this->returnValue($configValue)); - $this->assertEquals($expectedResult, $this->_model->isOutputEnabled('Module_One')); - } + $this->moduleList = $this->getMockBuilder(ModuleListInterface::class) + ->getMockForAbstractClass(); + $this->outputConfig = $this->getMockBuilder(ConfigInterface::class) + ->getMockForAbstractClass(); - public function isOutputEnabledGenericConfigPathDataProvider() - { - return ['output disabled' => [true, false], 'output enabled' => [false, true]]; + $this->model = new Manager( + $this->outputConfig, + $this->moduleList + ); } - /** - * @param bool $configValue - * @param bool $expectedResult - * @dataProvider isOutputEnabledCustomConfigPathDataProvider - */ - public function testIsOutputEnabledCustomConfigPath($configValue, $expectedResult) + public function testIsEnabled() { - $this->_moduleList->expects($this->once())->method('has')->will($this->returnValue(true)); - $this->_outputConfig->expects($this->at(0)) - ->method('isSetFlag') - ->with(self::XML_PATH_OUTPUT_ENABLED) - ->will($this->returnValue($configValue)); - $this->assertEquals($expectedResult, $this->_model->isOutputEnabled('Module_Two')); - } + $this->moduleList->expects($this->exactly(2)) + ->method('has') + ->willReturnMap( + [ + ['Module_Exists', true], + ['Module_NotExists', false], + ] + ); - public function isOutputEnabledCustomConfigPathDataProvider() - { - return [ - 'path literal, output disabled' => [false, false], - 'path literal, output enabled' => [true, true], - ]; + $this->assertTrue($this->model->isEnabled('Module_Exists')); + $this->assertFalse($this->model->isEnabled('Module_NotExists')); } } diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php index 5c5a64f53a086..d2a51746067ff 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/Mview/ActionFactory.php b/lib/internal/Magento/Framework/Mview/ActionFactory.php index 77b0c89c8cc39..af65ca15cb424 100644 --- a/lib/internal/Magento/Framework/Mview/ActionFactory.php +++ b/lib/internal/Magento/Framework/Mview/ActionFactory.php @@ -1,6 +1,6 @@ stateCollection = $stateCollection; $isCacheExists = $cache->test($cacheId); - parent::__construct($reader, $cache, $cacheId); + parent::__construct($reader, $cache, $cacheId, $serializer); if (!$isCacheExists) { $this->deleteNonexistentStates(); diff --git a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php index 694d88e63ae47..c2e4c75b433fa 100644 --- a/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php +++ b/lib/internal/Magento/Framework/Mview/Config/Data/Proxy.php @@ -1,6 +1,6 @@ [], 'view3' => []]; + private $views = ['view1' => [], 'view3' => []]; + + /** + * @var \Magento\Framework\Serialize\SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializerMock; protected function setUp() { @@ -58,28 +63,29 @@ protected function setUp() true, ['getItems'] ); + + $this->serializerMock = $this->getMock(\Magento\Framework\Serialize\SerializerInterface::class); } public function testConstructorWithCache() { $this->cache->expects($this->once())->method('test')->with($this->cacheId)->will($this->returnValue(true)); - $this->cache->expects( - $this->once() - )->method( - 'load' - )->with( - $this->cacheId - )->will( - $this->returnValue(serialize($this->views)) - ); + $this->cache->expects($this->once()) + ->method('load') + ->with($this->cacheId); $this->stateCollection->expects($this->never())->method('getItems'); - $this->model = new \Magento\Framework\Mview\Config\Data( + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->willReturn($this->views); + + $this->config = new \Magento\Framework\Mview\Config\Data( $this->reader, $this->cache, $this->stateCollection, - $this->cacheId + $this->cacheId, + $this->serializerMock ); } @@ -114,11 +120,12 @@ public function testConstructorWithoutCache() $this->stateCollection->expects($this->once())->method('getItems')->will($this->returnValue($states)); - $this->model = new \Magento\Framework\Mview\Config\Data( + $this->config = new \Magento\Framework\Mview\Config\Data( $this->reader, $this->cache, $this->stateCollection, - $this->cacheId + $this->cacheId, + $this->serializerMock ); } } diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/Config/ReaderTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/Config/ReaderTest.php index a0f8e377862d0..cb81c9932d366 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/Config/ReaderTest.php +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/Config/ReaderTest.php @@ -1,6 +1,6 @@ connectionMock = $this->getMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class, [], [], '', false); - $this->resourceMock = $this->getMock( - \Magento\Framework\App\ResourceConnection::class, - [], - [], - '', - false, - false + $this->resourceMock = $this->getMock( + \Magento\Framework\App\ResourceConnection::class, + [], + [], + '', + false, + false ); $this->connectionMock->expects($this->any()) @@ -57,19 +57,19 @@ protected function setUp() ->method('getConnection') ->willReturn($this->connectionMock); - $this->triggerFactoryMock = $this->getMock( + $this->triggerFactoryMock = $this->getMock( \Magento\Framework\DB\Ddl\TriggerFactory::class, [], [], '', false, false ); - $this->viewCollectionMock = $this->getMockForAbstractClass( + $this->viewCollectionMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\View\CollectionInterface::class, [], '', false, false, true, [] ); - $this->viewMock = $this->getMockForAbstractClass( + $this->viewMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\ViewInterface::class, [], '', false, false, true, [] ); $this->resourceMock->expects($this->any()) ->method('getTableName') - ->willReturn($this->tableName); + ->will($this->returnArgument(0)); $this->model = new Subscription( $this->resourceMock, @@ -96,11 +96,15 @@ public function testGetColumnName() $this->assertEquals('columnName', $this->model->getColumnName()); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function testCreate() { $triggerName = 'trigger_name'; $this->resourceMock->expects($this->atLeastOnce())->method('getTriggerName')->willReturn($triggerName); $triggerMock = $this->getMockBuilder(\Magento\Framework\DB\Ddl\Trigger::class) + ->setMethods(['setName', 'getName', 'setTime', 'setEvent', 'setTable', 'addStatement']) ->disableOriginalConstructor() ->getMock(); $triggerMock->expects($this->exactly(3)) @@ -121,11 +125,38 @@ public function testCreate() ->method('setTable') ->with($this->tableName) ->will($this->returnSelf()); - $triggerMock->expects($this->exactly(6)) + + $triggerMock->expects($this->at(4)) + ->method('addStatement') + ->with("INSERT IGNORE INTO test_view_cl (entity_id) VALUES (NEW.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(5)) + ->method('addStatement') + ->with("INSERT IGNORE INTO other_test_view_cl (entity_id) VALUES (NEW.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(11)) + ->method('addStatement') + ->with("INSERT IGNORE INTO test_view_cl (entity_id) VALUES (NEW.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(12)) + ->method('addStatement') + ->with("INSERT IGNORE INTO other_test_view_cl (entity_id) VALUES (NEW.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(18)) + ->method('addStatement') + ->with("INSERT IGNORE INTO test_view_cl (entity_id) VALUES (OLD.columnName);") + ->will($this->returnSelf()); + + $triggerMock->expects($this->at(19)) ->method('addStatement') + ->with("INSERT IGNORE INTO other_test_view_cl (entity_id) VALUES (OLD.columnName);") ->will($this->returnSelf()); - $changelogMock = $this->getMockForAbstractClass( + $changelogMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\View\ChangelogInterface::class, [], '', false, false, true, [] ); $changelogMock->expects($this->exactly(3)) @@ -143,7 +174,7 @@ public function testCreate() ->method('create') ->will($this->returnValue($triggerMock)); - $otherChangelogMock = $this->getMockForAbstractClass( + $otherChangelogMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\View\ChangelogInterface::class, [], '', false, false, true, [] ); $otherChangelogMock->expects($this->exactly(3)) @@ -153,7 +184,7 @@ public function testCreate() ->method('getColumnName') ->will($this->returnValue('entity_id')); - $otherViewMock = $this->getMockForAbstractClass( + $otherViewMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\ViewInterface::class, [], '', false, false, true, [] ); $otherViewMock->expects($this->exactly(1)) @@ -216,7 +247,7 @@ public function testRemove() ->method('create') ->will($this->returnValue($triggerMock)); - $otherChangelogMock = $this->getMockForAbstractClass( + $otherChangelogMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\View\ChangelogInterface::class, [], '', false, false, true, [] ); $otherChangelogMock->expects($this->exactly(3)) @@ -226,7 +257,7 @@ public function testRemove() ->method('getColumnName') ->will($this->returnValue('entity_id')); - $otherViewMock = $this->getMockForAbstractClass( + $otherViewMock = $this->getMockForAbstractClass( \Magento\Framework\Mview\ViewInterface::class, [], '', false, false, true, [] ); $otherViewMock->expects($this->exactly(1)) diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php index cf17286af0e9e..5371b8470bc66 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_merged_two.xml b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_merged_two.xml index 32d049f1570cf..030dc1b7c16fc 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_merged_two.xml +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_merged_two.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_one.xml b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_one.xml index 1ec8400e7f303..f6da086def77b 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_one.xml +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_one.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_three.xml b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_three.xml index a0ee312109cd4..340a12f306d1d 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_three.xml +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_three.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_two.xml b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_two.xml index 7e67bee354883..1553d12eaadb0 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_two.xml +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_two.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/valid_mview.xml b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/valid_mview.xml index d53d88943faf5..c4bba92f9955b 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/valid_mview.xml +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/valid_mview.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Mview/View.php b/lib/internal/Magento/Framework/Mview/View.php index 16d88767e508c..80b743b3f87fa 100644 --- a/lib/internal/Magento/Framework/Mview/View.php +++ b/lib/internal/Magento/Framework/Mview/View.php @@ -1,6 +1,6 @@ @@ -106,7 +106,7 @@ - Subscription model must be a valid PHP class or interface name. + DEPRECATED. Subscription model must be a valid PHP class or interface name. diff --git a/lib/internal/Magento/Framework/Notification/MessageInterface.php b/lib/internal/Magento/Framework/Notification/MessageInterface.php index cb5392b1114fe..70cf1637e412c 100644 --- a/lib/internal/Magento/Framework/Notification/MessageInterface.php +++ b/lib/internal/Magento/Framework/Notification/MessageInterface.php @@ -2,7 +2,7 @@ /** * System message * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/lib/internal/Magento/Framework/Notification/MessageList.php b/lib/internal/Magento/Framework/Notification/MessageList.php index 87e0905f89229..dcaa6e04618b8 100644 --- a/lib/internal/Magento/Framework/Notification/MessageList.php +++ b/lib/internal/Magento/Framework/Notification/MessageList.php @@ -1,6 +1,6 @@ arguments[$type])) { + if (array_key_exists($type, $this->arguments)) { if (is_string($this->arguments[$type])) { - $this->arguments[$type] = unserialize($this->arguments[$type]); + $this->arguments[$type] = $this->getSerializer()->unserialize($this->arguments[$type]); + } else if ($this->arguments[$type] === null) { + $this->arguments[$type] = []; } return $this->arguments[$type]; } else { - return [['_i_' => \Magento\Framework\ObjectManagerInterface::class]]; + return null; } } @@ -129,9 +143,15 @@ public function getPreference($type) */ public function extend(array $configuration) { - $this->arguments = $configuration['arguments']; - $this->virtualTypes = $configuration['instanceTypes']; - $this->preferences = $configuration['preferences']; + $this->arguments = isset($configuration['arguments']) + ? array_replace($this->arguments, $configuration['arguments']) + : $this->arguments; + $this->virtualTypes = isset($configuration['instanceTypes']) + ? array_replace($this->virtualTypes, $configuration['instanceTypes']) + : $this->virtualTypes; + $this->preferences = isset($configuration['preferences']) + ? array_replace($this->preferences, $configuration['preferences']) + : $this->preferences; } /** @@ -153,4 +173,19 @@ public function getPreferences() { return $this->preferences; } + + /** + * Get serializer + * + * @return SerializerInterface + * @deprecated + */ + private function getSerializer() + { + if (null === $this->serializer) { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() + ->get(Serialize::class); + } + return $this->serializer; + } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Config/Config.php b/lib/internal/Magento/Framework/ObjectManager/Config/Config.php index 5dc02e6ba7d97..e94699730cdcd 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Config/Config.php +++ b/lib/internal/Magento/Framework/ObjectManager/Config/Config.php @@ -1,10 +1,11 @@ _cache) { if (!$this->_currentCacheKey) { $this->_currentCacheKey = md5( - serialize([$this->_arguments, $this->_nonShared, $this->_preferences, $this->_virtualTypes]) + $this->getSerializer()->serialize( + [$this->_arguments, $this->_nonShared, $this->_preferences, $this->_virtualTypes] + ) ); } - $key = md5($this->_currentCacheKey . serialize($configuration)); + $key = md5($this->_currentCacheKey . $this->getSerializer()->serialize($configuration)); $cached = $this->_cache->get($key); if ($cached) { list( @@ -323,4 +331,19 @@ public function getPreferences() { return $this->_preferences; } + + /** + * Get serializer + * + * @return \Magento\Framework\Serialize\SerializerInterface + * @deprecated + */ + private function getSerializer() + { + if ($this->serializer === null) { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() + ->get(SerializerInterface::class); + } + return $this->serializer; + } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Config/Mapper/ArgumentParser.php b/lib/internal/Magento/Framework/ObjectManager/Config/Mapper/ArgumentParser.php index dc7463c0b1735..dfd2aa596b211 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Config/Mapper/ArgumentParser.php +++ b/lib/internal/Magento/Framework/ObjectManager/Config/Mapper/ArgumentParser.php @@ -1,6 +1,6 @@ _signatures, $this->_definitions) = $definitions; - $this->reader = $reader ?: new \Magento\Framework\Code\Reader\ClassReader(); - } - - /** - * Unpack signature - * - * @param string $signature - * @return mixed - */ - abstract protected function _unpack($signature); - - /** - * Get list of method parameters - * - * Retrieve an ordered list of constructor parameters. - * Each value is an array with following entries: - * - * array( - * 0, // string: Parameter name - * 1, // string|null: Parameter type - * 2, // bool: whether this param is required - * 3, // mixed: default value - * ); - * - * @param string $className - * @return array|null - */ - public function getParameters($className) - { - // if the definition isn't found in the list gathered from the compiled file then using reflection to find it - if (!array_key_exists($className, $this->_definitions)) { - return $this->reader->getConstructor($className); - } - - $definition = $this->_definitions[$className]; - if ($definition !== null) { - if (is_string($this->_signatures[$definition])) { - $this->_signatures[$definition] = $this->_unpack($this->_signatures[$definition]); - } - return $this->_signatures[$definition]; - } - return null; - } - - /** - * Retrieve list of all classes covered with definitions - * - * @return array - */ - public function getClasses() - { - return array_keys($this->_definitions); - } -} diff --git a/lib/internal/Magento/Framework/ObjectManager/Definition/Compiled/Binary.php b/lib/internal/Magento/Framework/ObjectManager/Definition/Compiled/Binary.php deleted file mode 100644 index bba816e072e6b..0000000000000 --- a/lib/internal/Magento/Framework/ObjectManager/Definition/Compiled/Binary.php +++ /dev/null @@ -1,27 +0,0 @@ - \Magento\Framework\ObjectManager\Definition\Compiled\Binary::class, - Serialized::MODE_NAME => \Magento\Framework\ObjectManager\Definition\Compiled\Serialized::class, - ]; - /** * @var \Magento\Framework\Code\Generator */ @@ -74,39 +38,26 @@ class DefinitionFactory /** * @param DriverInterface $filesystemDriver - * @param string $definitionDir * @param string $generationDir - * @param string $definitionFormat */ - public function __construct(DriverInterface $filesystemDriver, $definitionDir, $generationDir, $definitionFormat) - { + public function __construct( + DriverInterface $filesystemDriver, + $generationDir + ) { $this->_filesystemDriver = $filesystemDriver; - $this->_definitionDir = $definitionDir; $this->_generationDir = $generationDir; - $this->_definitionFormat = $definitionFormat; } /** * Create class definitions * - * @param mixed $definitions - * @return Runtime + * @return DefinitionInterface */ - public function createClassDefinition($definitions = false) + public function createClassDefinition() { - if ($definitions) { - if (is_string($definitions)) { - $definitions = $this->_unpack($definitions); - } - $definitionModel = self::$definitionClasses[$this->_definitionFormat]; - $result = new $definitionModel($definitions); - } else { - $autoloader = new \Magento\Framework\Code\Generator\Autoloader($this->getCodeGenerator()); - spl_autoload_register([$autoloader, 'load']); - - $result = new Runtime(); - } - return $result; + $autoloader = new Autoloader($this->getCodeGenerator()); + spl_autoload_register([$autoloader, 'load']); + return new Runtime(); } /** @@ -116,14 +67,7 @@ public function createClassDefinition($definitions = false) */ public function createPluginDefinition() { - $path = $this->_definitionDir . '/plugins.ser'; - if ($this->_filesystemDriver->isReadable($path)) { - return new \Magento\Framework\Interception\Definition\Compiled( - $this->_unpack($this->_filesystemDriver->fileGetContents($path)) - ); - } else { - return new \Magento\Framework\Interception\Definition\Runtime(); - } + return new \Magento\Framework\Interception\Definition\Runtime(); } /** @@ -133,36 +77,7 @@ public function createPluginDefinition() */ public function createRelations() { - $path = $this->_definitionDir . '/' . 'relations.ser'; - if ($this->_filesystemDriver->isReadable($path)) { - return new \Magento\Framework\ObjectManager\Relations\Compiled( - $this->_unpack($this->_filesystemDriver->fileGetContents($path)) - ); - } else { - return new \Magento\Framework\ObjectManager\Relations\Runtime(); - } - } - - /** - * Gets supported definition formats - * - * @return array - */ - public static function getSupportedFormats() - { - return array_keys(self::$definitionClasses); - } - - /** - * Un-compress definitions - * - * @param string $definitions - * @return mixed - */ - protected function _unpack($definitions) - { - $extractor = $this->_definitionFormat == Binary::MODE_NAME ? 'igbinary_unserialize' : 'unserialize'; - return $extractor($definitions); + return new \Magento\Framework\ObjectManager\Relations\Runtime(); } /** diff --git a/lib/internal/Magento/Framework/ObjectManager/DefinitionInterface.php b/lib/internal/Magento/Framework/ObjectManager/DefinitionInterface.php index f330c101b749f..9cd2cb1dec8a4 100644 --- a/lib/internal/Magento/Framework/ObjectManager/DefinitionInterface.php +++ b/lib/internal/Magento/Framework/ObjectManager/DefinitionInterface.php @@ -2,7 +2,7 @@ /** * Object Manager class definition interface * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\ObjectManager; diff --git a/lib/internal/Magento/Framework/ObjectManager/DynamicConfigInterface.php b/lib/internal/Magento/Framework/ObjectManager/DynamicConfigInterface.php index 319572c6fd8a9..dd399c33ccb1f 100644 --- a/lib/internal/Magento/Framework/ObjectManager/DynamicConfigInterface.php +++ b/lib/internal/Magento/Framework/ObjectManager/DynamicConfigInterface.php @@ -1,6 +1,6 @@ config = $config; $this->objectManager = $objectManager; - $this->definitions = $definitions ?: new \Magento\Framework\ObjectManager\Definition\Runtime(); + $this->definitions = $definitions ?: $this->getDefinitions(); $this->globalArguments = $globalArguments; } @@ -79,6 +86,17 @@ public function setArguments($arguments) $this->globalArguments = $arguments; } + /** + * @return \Magento\Framework\ObjectManager\DefinitionInterface + */ + public function getDefinitions() + { + if ($this->definitions === null) { + $this->definitions = new \Magento\Framework\ObjectManager\Definition\Runtime(); + } + return $this->definitions; + } + /** * Create object * @@ -177,4 +195,44 @@ protected function parseArray(&$array) } } } + + /** + * Resolve constructor arguments + * + * @param string $requestedType + * @param array $parameters + * @param array $arguments + * + * @return array + * + * @throws \UnexpectedValueException + * @throws \BadMethodCallException + */ + protected function resolveArgumentsInRuntime($requestedType, array $parameters, array $arguments = []) + { + $resolvedArguments = []; + foreach ($parameters as $parameter) { + list($paramName, $paramType, $paramRequired, $paramDefault) = $parameter; + $argument = null; + if (!empty($arguments) && (isset($arguments[$paramName]) || array_key_exists($paramName, $arguments))) { + $argument = $arguments[$paramName]; + } elseif ($paramRequired) { + if ($paramType) { + $argument = ['instance' => $paramType]; + } else { + $this->creationStack = []; + throw new \BadMethodCallException( + 'Missing required argument $' . $paramName . ' of ' . $requestedType . '.' + ); + } + } else { + $argument = $paramDefault; + } + + $this->resolveArgument($argument, $paramType, $paramDefault, $paramName, $requestedType); + + $resolvedArguments[] = $argument; + } + return $resolvedArguments; + } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/Compiled.php b/lib/internal/Magento/Framework/ObjectManager/Factory/Compiled.php index 84b63bd74c745..845a2e60c5494 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/Compiled.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/Compiled.php @@ -1,7 +1,6 @@ config->getArguments($requestedType); $type = $this->config->getInstanceType($requestedType); - if (!$args) { + if ($args === []) { + // Case 1: no arguments required return new $type(); - } - - foreach ($args as $key => &$argument) { - if (isset($arguments[$key])) { - $argument = $arguments[$key]; - } elseif (isset($argument['_i_'])) { - $argument = $this->get($argument['_i_']); - } elseif (isset($argument['_ins_'])) { - $argument = $this->create($argument['_ins_']); - } elseif (isset($argument['_v_'])) { - $argument = $argument['_v_']; - } elseif (isset($argument['_vac_'])) { - $argument = $argument['_vac_']; - $this->parseArray($argument); - } elseif (isset($argument['_vn_'])) { - $argument = null; - } elseif (isset($argument['_a_'])) { - if (isset($this->globalArguments[$argument['_a_']])) { - $argument = $this->globalArguments[$argument['_a_']]; - } else { - $argument = $argument['_d_']; + } else if ($args !== null) { + /** + * Case 2: arguments retrieved from pre-compiled DI cache + * + * Argument key meanings: + * + * _i_: shared instance of a class or interface + * _ins_: non-shared instance of a class or interface + * _v_: non-array literal value + * _vac_: array, may be nested and contain other types of keys listed here (objects, array, nulls, etc) + * _vn_: null value + * _a_: value to be taken from named environment variable + * _d_: default value in case environment variable specified by _a_ does not exist + */ + foreach ($args as $key => &$argument) { + if (isset($arguments[$key])) { + $argument = $arguments[$key]; + } elseif (isset($argument['_i_'])) { + $argument = $this->get($argument['_i_']); + } elseif (isset($argument['_ins_'])) { + $argument = $this->create($argument['_ins_']); + } elseif (isset($argument['_v_'])) { + $argument = $argument['_v_']; + } elseif (isset($argument['_vac_'])) { + $argument = $argument['_vac_']; + $this->parseArray($argument); + } elseif (isset($argument['_vn_'])) { + $argument = null; + } elseif (isset($argument['_a_'])) { + if (isset($this->globalArguments[$argument['_a_']])) { + $argument = $this->globalArguments[$argument['_a_']]; + } else { + $argument = $argument['_d_']; + } } } + $args = array_values($args); + } else { + // Case 3: arguments retrieved in runtime + $parameters = $this->getDefinitions()->getParameters($type) ?: []; + $args = $this->resolveArgumentsInRuntime( + $type, + $parameters, + $arguments + ); } - $args = array_values($args); - return $this->createObject($type, $args); } diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/Dynamic/Developer.php b/lib/internal/Magento/Framework/ObjectManager/Factory/Dynamic/Developer.php index 909df40762de4..5d4600e604fb0 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/Dynamic/Developer.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/Dynamic/Developer.php @@ -1,19 +1,12 @@ config->getArguments($requestedType), $arguments) - : $this->config->getArguments($requestedType); - foreach ($parameters as $parameter) { - list($paramName, $paramType, $paramRequired, $paramDefault) = $parameter; - $argument = null; - if (!empty($arguments) && (isset($arguments[$paramName]) || array_key_exists($paramName, $arguments))) { - $argument = $arguments[$paramName]; - } elseif ($paramRequired) { - if ($paramType) { - $argument = ['instance' => $paramType]; - } else { - $this->creationStack = []; - throw new \BadMethodCallException( - 'Missing required argument $' . $paramName . ' of ' . $requestedType . '.' - ); - } + // Get default arguments from config, merge with supplied arguments + $defaultArguments = $this->config->getArguments($requestedType); + if (is_array($defaultArguments)) { + if (count($arguments)) { + $arguments = array_replace($defaultArguments, $arguments); } else { - $argument = $paramDefault; + $arguments = $defaultArguments; } - - $this->resolveArgument($argument, $paramType, $paramDefault, $paramName, $requestedType); - - $resolvedArguments[] = $argument; } - return $resolvedArguments; + + return $this->resolveArgumentsInRuntime($requestedType, $parameters, $arguments); } /** diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/Dynamic/Production.php b/lib/internal/Magento/Framework/ObjectManager/Factory/Dynamic/Production.php index daafcee74f873..9814ed1283928 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/Dynamic/Production.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/Dynamic/Production.php @@ -1,6 +1,6 @@ _relations = $relations; - } - - /** - * Check whether requested type is available for read - * - * @param string $type - * @return bool - */ - public function has($type) - { - return isset($this->_relations[$type]); - } - - /** - * Retrieve parents for class - * - * @param string $type - * @return array - */ - public function getParents($type) - { - return $this->_relations[$type]; - } -} diff --git a/lib/internal/Magento/Framework/ObjectManager/Relations/Runtime.php b/lib/internal/Magento/Framework/ObjectManager/Relations/Runtime.php index 7e6ac27814541..92d72b14c120d 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Relations/Runtime.php +++ b/lib/internal/Magento/Framework/ObjectManager/Relations/Runtime.php @@ -1,6 +1,6 @@ objectManager = new ObjectManager($this); + $this->serializerMock = $this->getMock(SerializerInterface::class); + + $initialData = [ + 'arguments' => [ + 'type1' => 'initial serialized configuration for type1', + 'class_with_no_arguments_serialized' => null, + 'class_with_arguments_serialized' => 'serialized arguments', + 'class_with_arguments_unserialized' => ['unserialized', 'arguments'], + 'class_with_no_arguments_unserialized' => [], + ], + 'instanceTypes' => [ + 'instanceType1' => 'instanceTypeValue1', + 'instanceType2' => 'instanceTypeValue2' + ], + 'preferences' => [ + 'preference1' => 'preferenceValue1', + 'preference2' => 'preferenceValue2' + ] + ]; + + $this->compiled = $this->objectManager->getObject( + Compiled::class, + [ + 'data' => $initialData, + 'serializer' => $this->serializerMock + ] + ); + } + + public function testExtend() + { + + $configuration = [ + 'arguments' => [ + 'type1' => 'serialized configuration for type1', + 'type2' => 'serialized configuration for type2' + ], + 'instanceTypes' => [ + 'instanceType2' => 'newInstanceTypeValue2', + 'instanceType3' => 'newInstanceTypeValue3' + ], + 'preferences' => [ + 'preference1' => 'newPreferenceValue1' + ] + ]; + $expectedArguments = [ + 'type1' => [ + 'argument1_1' => 'newArgumentValue1_1' + ], + 'type2' => [ + 'argument2_1' => 'newArgumentValue2_1' + ] + ]; + $expectedVirtualTypes = [ + 'instanceType1' => 'instanceTypeValue1', + 'instanceType2' => 'newInstanceTypeValue2', + 'instanceType3' => 'newInstanceTypeValue3' + ]; + $expectedPreferences = [ + 'preference1' => 'newPreferenceValue1', + 'preference2' => 'preferenceValue2' + ]; + + $this->serializerMock->expects($this->at(0)) + ->method('unserialize') + ->with($configuration['arguments']['type1']) + ->willReturn($expectedArguments['type1']); + $this->serializerMock->expects($this->at(1)) + ->method('unserialize') + ->with($configuration['arguments']['type2']) + ->willReturn($expectedArguments['type2']); + + $this->compiled->extend($configuration); + foreach ($expectedArguments as $type => $arguments) { + $this->assertEquals($arguments, $this->compiled->getArguments($type)); + } + $this->assertEquals($expectedVirtualTypes, $this->compiled->getVirtualTypes()); + $this->assertEquals($expectedPreferences, $this->compiled->getPreferences()); + } + + /** + * Arguments defined in array, have not previously been unserialized + */ + public function testGetArgumentsSerialized() + { + $unserializedArguments = ['unserialized', 'arguments']; + + // method called twice but after one unserialization, unserialized version should be stored + $this->serializerMock->expects($this->once())->method('unserialize') + ->with('serialized arguments') + ->willReturn($unserializedArguments); + + $this->assertSame($unserializedArguments, $this->compiled->getArguments('class_with_arguments_serialized')); + $this->assertSame($unserializedArguments, $this->compiled->getArguments('class_with_arguments_serialized')); + } + + /** + * Arguments defined in array, have not previously been unserialized + */ + public function testGetArgumentsSerializedEmpty() + { + $this->serializerMock->expects($this->never())->method('unserialize'); + $this->assertSame([], $this->compiled->getArguments('class_with_no_arguments_serialized')); + } + + /** + * Arguments defined in array, have previously been unserialized + */ + public function testGetArgumentsUnserialized() + { + $unserializedArguments = ['unserialized', 'arguments']; + $this->serializerMock->expects($this->never())->method('unserialize'); + $this->assertSame($unserializedArguments, $this->compiled->getArguments('class_with_arguments_unserialized')); + } + + /** + * Arguments are defined but empty + */ + public function testGetArgumentsUnserializedEmpty() + { + $this->serializerMock->expects($this->never())->method('unserialize'); + $this->assertSame([], $this->compiled->getArguments('class_with_no_arguments_unserialized')); + } + + /** + * Arguments not defined in array + */ + public function testGetArgumentsNotDefined() + { + $this->serializerMock->expects($this->never())->method('unserialize'); + $this->assertSame(null, $this->compiled->getArguments('class_not_stored_in_config')); + } +} diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/ConfigTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/ConfigTest.php index 844d5fa94a627..29561991f28d0 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/ConfigTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/ConfigTest.php @@ -1,14 +1,23 @@ objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + } + public function testGetArgumentsEmpty() { $config = new Config(); @@ -42,6 +51,14 @@ public function testExtendWithCacheMock() $cache->expects($this->once())->method('get')->will($this->returnValue(false)); $config = new Config(null, $definitions); + $serializerMock = $this->getMock(SerializerInterface::class); + $serializerMock->expects($this->exactly(2)) + ->method('serialize'); + $this->objectManagerHelper->setBackwardCompatibleProperty( + $config, + 'serializer', + $serializerMock + ); $config->setCache($cache); $this->_assertFooTypeArguments($config); diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Mapper/ArgumentParserTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Mapper/ArgumentParserTest.php index d3907cfece779..c38a8e11f7c78 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Mapper/ArgumentParserTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Mapper/ArgumentParserTest.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Mapper/_files/mapped_simple_di_config.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Mapper/_files/mapped_simple_di_config.php index dd94048d59d84..eb20fa307f718 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Mapper/_files/mapped_simple_di_config.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Mapper/_files/mapped_simple_di_config.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Reader/DomFactoryTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Reader/DomFactoryTest.php index 2f5c6027d0f1f..1ec8c28e20390 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Reader/DomFactoryTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/Reader/DomFactoryTest.php @@ -1,6 +1,6 @@ [ + ' + + + + + Some_Class_Name + + + + ', + [ + "Element 'item', attribute 'sortOrder': 'false' is not a valid value of the atomic type 'xs:integer'." . + "\nLine: 6\n" + ], + ], ]; diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/_files/valid_config.xml b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/_files/valid_config.xml index df900f0ce4a9c..852a3b3163339 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/_files/valid_config.xml +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/_files/valid_config.xml @@ -1,7 +1,7 @@ @@ -57,6 +57,11 @@ some_value + + Instance_test_name_two + Instance_test_name_one + Instance_test_name_three + diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/Compiled/BinaryTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/Compiled/BinaryTest.php deleted file mode 100644 index 80cf73a44e7ac..0000000000000 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/Compiled/BinaryTest.php +++ /dev/null @@ -1,21 +0,0 @@ -markTestSkipped('This test requires igbinary PHP extension'); - } - $checkString = 'packed code'; - $signatures = ['wonderfulClass' => igbinary_serialize($checkString)]; - $definitions = ['wonderful' => 'wonderfulClass']; - $model = new \Magento\Framework\ObjectManager\Definition\Compiled\Binary([$signatures, $definitions]); - $this->assertEquals($checkString, $model->getParameters('wonderful')); - } -} diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/Compiled/SerializedTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/Compiled/SerializedTest.php deleted file mode 100644 index 9454377c3b720..0000000000000 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/Compiled/SerializedTest.php +++ /dev/null @@ -1,35 +0,0 @@ - null]; - $model = new \Magento\Framework\ObjectManager\Definition\Compiled\Serialized([$signatures, $definitions]); - $this->assertEquals(null, $model->getParameters('wonderful')); - } - - public function testGetParametersWithSignatureObject() - { - $wonderfulSignature = new \stdClass(); - $signatures = ['wonderfulClass' => $wonderfulSignature]; - $definitions = ['wonderful' => 'wonderfulClass']; - $model = new \Magento\Framework\ObjectManager\Definition\Compiled\Serialized([$signatures, $definitions]); - $this->assertEquals($wonderfulSignature, $model->getParameters('wonderful')); - } - - public function testGetParametersWithUnpacking() - { - $checkString = 'code to pack'; - $signatures = ['wonderfulClass' => serialize($checkString)]; - $definitions = ['wonderful' => 'wonderfulClass']; - $model = new \Magento\Framework\ObjectManager\Definition\Compiled\Serialized([$signatures, $definitions]); - $this->assertEquals($checkString, $model->getParameters('wonderful')); - } -} diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/CompiledStub.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/CompiledStub.php deleted file mode 100644 index 16b8436a0c41a..0000000000000 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Definition/CompiledStub.php +++ /dev/null @@ -1,25 +0,0 @@ -getMock( - \Magento\Framework\Code\Reader\ClassReader::class, - ['getConstructor'], - [], - '', - false - ); - $readerMock->expects($this->once()) - ->method('getConstructor') - ->with($className) - ->willReturn($undefinedDefinitionSignature); - $model = $objectManager->getObject( - \Magento\Framework\ObjectManager\Test\Unit\Definition\CompiledStub::class, - [ - 'definitions' => [[], []], - 'reader' => $readerMock - ] - ); - $this->assertEquals($undefinedDefinitionSignature, $model->getParameters($className)); - } -} diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/DefinitionFactoryTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/DefinitionFactoryTest.php index 468e01ce80aba..9d61b925e2141 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/DefinitionFactoryTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/DefinitionFactoryTest.php @@ -1,121 +1,58 @@ sampleContent = serialize([1, 2, 3]); - $this->filesystemDriverMock = $this->getMock( - \Magento\Framework\Filesystem\Driver\File::class, - [], - [], - '', - false - ); - $this->model = new \Magento\Framework\ObjectManager\DefinitionFactory( + $this->filesystemDriverMock = $this->getMock(File::class); + $this->definitionFactory = new DefinitionFactory( $this->filesystemDriverMock, - 'DefinitionDir', - 'GenerationDir', - Serialized::MODE_NAME + 'generation dir' ); } - public function testCreateClassDefinitionFromString() + public function testCreateClassDefinition() { $this->assertInstanceOf( - \Magento\Framework\ObjectManager\Definition\Compiled\Serialized::class, - $this->model->createClassDefinition($this->sampleContent) + DefinitionInterface::class, + $this->definitionFactory->createClassDefinition() ); } - /** - * @param string $path - * @param string $callMethod - * @param string $expectedClass - * @dataProvider createPluginsAndRelationsReadableDataProvider - */ - public function testCreatePluginsAndRelationsReadable($path, $callMethod, $expectedClass) - { - $this->filesystemDriverMock->expects($this->once())->method('isReadable') - ->with($path) - ->will($this->returnValue(true)); - $this->filesystemDriverMock->expects($this->once())->method('fileGetContents') - ->with($path) - ->will($this->returnValue($this->sampleContent)); - $this->assertInstanceOf($expectedClass, $this->model->$callMethod()); - } - - public function createPluginsAndRelationsReadableDataProvider() - { - return [ - 'relations' => [ - 'DefinitionDir/relations.ser', - 'createRelations', \Magento\Framework\ObjectManager\Relations\Compiled::class, - ], - 'plugins' => [ - 'DefinitionDir/plugins.ser', - 'createPluginDefinition', \Magento\Framework\Interception\Definition\Compiled::class, - ], - ]; - } - - /** - * @param string $path - * @param string $callMethod - * @param string $expectedClass - * @dataProvider createPluginsAndRelationsNotReadableDataProvider - */ - public function testCreatePluginsAndRelationsNotReadable($path, $callMethod, $expectedClass) + public function testCreatePluginDefinition() { - $this->filesystemDriverMock->expects($this->once())->method('isReadable') - ->with($path) - ->will($this->returnValue(false)); - $this->assertInstanceOf($expectedClass, $this->model->$callMethod()); - } - - public function createPluginsAndRelationsNotReadableDataProvider() - { - return [ - 'relations' => [ - 'DefinitionDir/relations.ser', - 'createRelations', \Magento\Framework\ObjectManager\Relations\Runtime::class, - ], - 'plugins' => [ - 'DefinitionDir/plugins.ser', - 'createPluginDefinition', \Magento\Framework\Interception\Definition\Runtime::class, - ], - ]; + $this->assertInstanceOf( + InterceptionDefinitionInterface::class, + $this->definitionFactory->createPluginDefinition() + ); } - public function testGetSupportedFormats() + public function testCreateRelations() { - $actual = \Magento\Framework\ObjectManager\DefinitionFactory::getSupportedFormats(); - $this->assertInternalType('array', $actual); - foreach ($actual as $className) { - $this->assertInternalType('string', $className); - } + $this->assertInstanceOf( + RelationsInterface::class, + $this->definitionFactory->createRelations() + ); } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php index dbf7da6bb5eab..6cd28c5177c10 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php @@ -1,52 +1,60 @@ objectManager = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) + $this->objectManager = new ObjectManager($this); + $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) ->setMethods([]) ->getMock(); - $this->config = $this->getMockBuilder(\Magento\Framework\ObjectManager\ConfigInterface::class) + $this->config = $this->getMockBuilder(ConfigInterface::class) ->setMethods([]) ->getMock(); $this->sharedInstances = []; $this->factory = new Compiled($this->config, $this->sharedInstances, []); - $this->factory->setObjectManager($this->objectManager); + $this->factory->setObjectManager($this->objectManagerMock); + + $this->definitionsMock = $this->getMockBuilder(DefinitionInterface::class)->getMock(); + $this->objectManager->setBackwardCompatibleProperty($this->factory, 'definitions', $this->definitionsMock); } public function testCreateSimple() @@ -54,10 +62,9 @@ public function testCreateSimple() $expectedConfig = $this->getSimpleConfig(); $requestedType = 'requestedType'; - $type = \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\SimpleClassTesting::class; - $sharedType = - \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\DependencySharedTesting::class; - $nonSharedType = \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\DependencyTesting::class; + $type = SimpleClassTesting::class; + $sharedType = DependencySharedTesting::class; + $nonSharedType = DependencyTesting::class; $this->config->expects($this->any()) ->method('getArguments') @@ -84,11 +91,11 @@ public function testCreateSimple() ] ); - /** @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\SimpleClassTesting $result */ + /** @var SimpleClassTesting $result */ $result = $this->factory->create($requestedType, []); $this->assertInstanceOf( - \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\SimpleClassTesting::class, + SimpleClassTesting::class, $result ); $this->assertInstanceOf($sharedType, $result->getSharedDependency()); @@ -103,11 +110,11 @@ public function testCreateSimpleConfiguredArguments() { $expectedConfig = $this->getSimpleNestedConfig(); - $type = \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\SimpleClassTesting::class; + $type = SimpleClassTesting::class; $requestedType = 'requestedType'; $sharedType = - \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\DependencySharedTesting::class; - $nonSharedType = \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\DependencyTesting::class; + DependencySharedTesting::class; + $nonSharedType = DependencyTesting::class; $this->config->expects($this->any()) ->method('getArguments') @@ -135,11 +142,11 @@ public function testCreateSimpleConfiguredArguments() ] ); - /** @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\SimpleClassTesting $result */ + /** @var SimpleClassTesting $result */ $result = $this->factory->create($requestedType, []); $this->assertInstanceOf( - \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\SimpleClassTesting::class, + SimpleClassTesting::class, $result ); $this->assertInstanceOf($sharedType, $result->getSharedDependency()); @@ -163,8 +170,45 @@ public function testCreateSimpleConfiguredArguments() $this->assertNull($result->getNullValue()); } + public function testCreateGetArgumentsInRuntime() + { + // Stub OM to create test assets + $this->config->expects($this->any())->method('isShared')->willReturn(true); + $this->objectManagerMock->expects($this->any())->method('get')->willReturnMap( + [ + [DependencyTesting::class, new DependencyTesting()], + [DependencySharedTesting::class, new DependencySharedTesting()] + ] + ); + + // Simulate case where compiled DI config not found + $type = SimpleClassTesting::class; + $this->config->expects($this->any())->method('getArguments')->willReturn(null); + $this->config->expects($this->any())->method('getInstanceType')->willReturnArgument(0); + $this->definitionsMock->expects($this->once()) + ->method('getParameters') + ->with($type) + ->willReturn($this->getRuntimeParameters()); + + $sharedType = DependencySharedTesting::class; + $nonSharedType = DependencyTesting::class; + + // Run SUT + /** @var SimpleClassTesting $result */ + $result = $this->factory->create($type, []); + + $this->assertInstanceOf($type, $result); + $this->assertInstanceOf($sharedType, $result->getSharedDependency()); + $this->assertInstanceOf($nonSharedType, $result->getNonSharedDependency()); + $this->assertEquals('value', $result->getValue()); + $this->assertEquals(['default_value1', 'default_value2'], $result->getValueArray()); + $this->assertEquals(null, $result->getGlobalValue()); + $this->assertNull($result->getNullValue()); + } + /** - * Returns simple config + * Returns simple config with default constructor values for + * \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\SimpleClassTesting * * @return array */ @@ -172,11 +216,11 @@ private function getSimpleConfig() { return [ 'nonSharedDependency' => [ - '_ins_' => \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\DependencyTesting::class, + '_ins_' => DependencyTesting::class, ], 'sharedDependency' => [ '_i_' => - \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\DependencySharedTesting::class, + DependencySharedTesting::class, ], 'value' => [ '_v_' => 'value', @@ -195,7 +239,8 @@ private function getSimpleConfig() } /** - * Returns nested config + * Returns config for \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\SimpleClassTesting + * with non-default nested array value for the $value_array parameter * * @return array */ @@ -203,11 +248,11 @@ private function getSimpleNestedConfig() { return [ 'nonSharedDependency' => [ - '_ins_' => \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\DependencyTesting::class, + '_ins_' => DependencyTesting::class, ], 'sharedDependency' => [ '_i_' => - \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\DependencySharedTesting::class, + DependencySharedTesting::class, ], 'value' => [ '_v_' => 'value', @@ -249,4 +294,63 @@ private function getSimpleNestedConfig() ] ]; } + + /** + * Returns mock parameter list for + * \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Compiled\SimpleClassTesting + * as would be found by \Magento\Framework\ObjectManager\DefinitionInterface + * + * @return array + */ + private function getRuntimeParameters() + { + return [ + 0 => + [ + 0 => 'nonSharedDependency', + 1 => DependencyTesting::class, + 2 => true, + 3 => null, + ], + 1 => + [ + 0 => 'sharedDependency', + 1 => DependencySharedTesting::class, + 2 => true, + 3 => null, + ], + 2 => + [ + 0 => 'value', + 1 => null, + 2 => false, + 3 => 'value', + ], + 3 => + [ + 0 => 'valueArray', + 1 => null, + 2 => false, + 3 => + [ + 0 => 'default_value1', + 1 => 'default_value2', + ], + ], + 4 => + [ + 0 => 'globalValue', + 1 => null, + 2 => false, + 3 => '', + ], + 5 => + [ + 0 => 'nullValue', + 1 => null, + 2 => false, + 3 => null, + ], + ]; + } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php index 91fbe465862f1..bfeae6cedfe9b 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php @@ -1,6 +1,6 @@ 'yes']; - - $model = new \Magento\Framework\ObjectManager\Relations\Compiled($relations); - $this->assertEquals(true, $model->has('amazing')); - $this->assertEquals(false, $model->has('fuzzy')); - } - - public function testGetParents() - { - $relations = ['amazing' => 'parents']; - - $model = new \Magento\Framework\ObjectManager\Relations\Compiled($relations); - $this->assertEquals('parents', $model->getParents('amazing')); - } -} diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Relations/RuntimeTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Relations/RuntimeTest.php index cb274f845b668..6c66dde1e80cd 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Relations/RuntimeTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Relations/RuntimeTest.php @@ -1,6 +1,6 @@ @@ -20,6 +20,7 @@ + diff --git a/lib/internal/Magento/Framework/ObjectManagerInterface.php b/lib/internal/Magento/Framework/ObjectManagerInterface.php index e30b040866b74..6041f63d1e7d4 100644 --- a/lib/internal/Magento/Framework/ObjectManagerInterface.php +++ b/lib/internal/Magento/Framework/ObjectManagerInterface.php @@ -1,6 +1,6 @@ with($this->equalTo($totalAmount), $this->equalTo($expectedAdjustments)) ->will($this->returnValue($amountBaseMock)); - $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $productMock = $this->getMockBuilder(\Magento\Framework\Pricing\SaleableInterface::class) ->disableOriginalConstructor() ->setMethods(['getPriceInfo', '__wakeup']) - ->getMock(); + ->getMockForAbstractClass(); - $weeeAdjustmentMock = $this->getMockBuilder(\Magento\Weee\Pricing\Adjustment::class) + $weeeAdjustmentMock = $this->getMockBuilder(\Magento\Framework\Pricing\Adjustment\AdjustmentInterface::class) ->disableOriginalConstructor() ->getMock(); $weeeAdjustmentMock->expects($this->once()) @@ -82,7 +82,7 @@ public function testGetAmount() ->with($this->equalTo($amountInclTax), $this->equalTo($productMock)) ->will($this->returnValue($weeeAdjustment + $amountInclTax)); - $taxAdjustmentMock = $this->getMockBuilder(\Magento\Tax\Pricing\Adjustment::class) + $taxAdjustmentMock = $this->getMockBuilder(\Magento\Framework\Pricing\Adjustment\AdjustmentInterface::class) ->disableOriginalConstructor() ->getMock(); $taxAdjustmentMock->expects($this->once()) @@ -127,12 +127,12 @@ public function testGetAmountExclude() $adjustment = 5; $expectedAdjustments = []; - $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $productMock = $this->getMockBuilder(\Magento\Framework\Pricing\SaleableInterface::class) ->disableOriginalConstructor() ->setMethods(['getPriceInfo', '__wakeup']) - ->getMock(); + ->getMockForAbstractClass(); - $taxAdjustmentMock = $this->getMockBuilder(\Magento\Tax\Pricing\Adjustment::class) + $taxAdjustmentMock = $this->getMockBuilder(\Magento\Framework\Pricing\Adjustment\AdjustmentInterface::class) ->disableOriginalConstructor() ->getMock(); $taxAdjustmentMock->expects($this->once()) @@ -150,7 +150,7 @@ public function testGetAmountExclude() ->with($this->equalTo($fullamount), $this->equalTo($productMock)) ->will($this->returnValue($amount)); - $weeeAdjustmentMock = $this->getMockBuilder(\Magento\Weee\Pricing\Adjustment::class) + $weeeAdjustmentMock = $this->getMockBuilder(\Magento\Framework\Pricing\Adjustment\AdjustmentInterface::class) ->disableOriginalConstructor() ->getMock(); $weeeAdjustmentMock->expects($this->once()) diff --git a/lib/internal/Magento/Framework/Pricing/Test/Unit/Adjustment/CollectionTest.php b/lib/internal/Magento/Framework/Pricing/Test/Unit/Adjustment/CollectionTest.php index a156104b57388..ade0b98b835be 100644 --- a/lib/internal/Magento/Framework/Pricing/Test/Unit/Adjustment/CollectionTest.php +++ b/lib/internal/Magento/Framework/Pricing/Test/Unit/Adjustment/CollectionTest.php @@ -1,6 +1,6 @@ assertEquals($this->rendererPool, $this->model->getRendererPool()); } + + /** + * This tests ensures that protected method getCacheLifetime() returns a null value when cacheLifeTime is not + * explicitly set in the parent block + */ + public function testCacheLifetime() + { + $reflectionClass = new \ReflectionClass(get_class($this->model)); + $methodReflection = $reflectionClass->getMethod('getCacheLifetime'); + $methodReflection->setAccessible(true); + $cacheLifeTime = $methodReflection->invoke($this->model); + $this->assertNull($cacheLifeTime, 'Expected null cache lifetime'); + } } diff --git a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/RendererPoolTest.php b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/RendererPoolTest.php index 7d400b6933c54..5d16612d61045 100644 --- a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/RendererPoolTest.php +++ b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/RendererPoolTest.php @@ -1,6 +1,6 @@ disableOriginalConstructor() ->getMock(); - $this->price = $this->getMockBuilder(\Magento\Catalog\Pricing\Price\BasePrice::class) + $this->price = $this->getMockBuilder(\Magento\Framework\Pricing\Price\PriceInterface::class) ->disableOriginalConstructor() - ->getMock(); + ->getMockForAbstractClass(); $this->amount = $this->getMockBuilder(\Magento\Framework\Pricing\Amount\Base::class) ->disableOriginalConstructor() ->getMock(); - $this->saleableItem = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $this->saleableItem = $this->getMockBuilder(\Magento\Framework\Pricing\SaleableInterface::class) ->disableOriginalConstructor() - ->getMock(); + ->getMockForAbstractClass(); $this->renderPool = $this->getMockBuilder(\Magento\Framework\Pricing\Render\RendererPool::class) ->disableOriginalConstructor() diff --git a/lib/internal/Magento/Framework/Process/PhpExecutableFinderFactory.php b/lib/internal/Magento/Framework/Process/PhpExecutableFinderFactory.php index 4780fd408d413..8328d021dc298 100644 --- a/lib/internal/Magento/Framework/Process/PhpExecutableFinderFactory.php +++ b/lib/internal/Magento/Framework/Process/PhpExecutableFinderFactory.php @@ -1,6 +1,6 @@ serviceInterfaceMethodsMap[$key])) { $methodMap = $this->cache->load($key); if ($methodMap) { - $this->serviceInterfaceMethodsMap[$key] = unserialize($methodMap); + $this->serviceInterfaceMethodsMap[$key] = $this->getSerializer()->unserialize($methodMap); } else { $methodMap = $this->getMethodMapViaReflection($interfaceName); $this->serviceInterfaceMethodsMap[$key] = $methodMap; - $this->cache->save(serialize($this->serviceInterfaceMethodsMap[$key]), $key); + $this->cache->save($this->getSerializer()->serialize($this->serviceInterfaceMethodsMap[$key]), $key); } } return $this->serviceInterfaceMethodsMap[$key]; @@ -117,7 +123,7 @@ public function getMethodParams($serviceClassName, $serviceMethodName) $cacheId = self::SERVICE_METHOD_PARAMS_CACHE_PREFIX . hash('md5', $serviceClassName . $serviceMethodName); $params = $this->cache->load($cacheId); if ($params !== false) { - return unserialize($params); + return $this->getSerializer()->unserialize($params); } $serviceClass = new ClassReflection($serviceClassName); /** @var MethodReflection $serviceMethod */ @@ -133,7 +139,7 @@ public function getMethodParams($serviceClassName, $serviceMethodName) self::METHOD_META_DEFAULT_VALUE => $isDefaultValueAvailable ? $paramReflection->getDefaultValue() : null ]; } - $this->cache->save(serialize($params), $cacheId, [ReflectionCache::CACHE_TAG]); + $this->cache->save($this->getSerializer()->serialize($params), $cacheId, [ReflectionCache::CACHE_TAG]); return $params; } @@ -217,4 +223,19 @@ public function isMethodReturnValueRequired($type, $methodName) $methods = $this->getMethodsMap($type); return $methods[$methodName]['isRequired']; } + + /** + * Get serializer + * + * @return \Magento\Framework\Serialize\SerializerInterface + * @deprecated + */ + private function getSerializer() + { + if ($this->serializer === null) { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() + ->get(SerializerInterface::class); + } + return $this->serializer; + } } diff --git a/lib/internal/Magento/Framework/Reflection/NameFinder.php b/lib/internal/Magento/Framework/Reflection/NameFinder.php index 9de6125cf1d3d..4172d3f2715ae 100644 --- a/lib/internal/Magento/Framework/Reflection/NameFinder.php +++ b/lib/internal/Magento/Framework/Reflection/NameFinder.php @@ -1,6 +1,6 @@ getMockForAbstractClass(); $fieldNamerMock = $this->getMockBuilder(\Magento\Framework\Reflection\FieldNamer::class) ->getMockForAbstractClass(); - $this->model = $objectManager->getObject( + $this->object = $objectManager->getObject( \Magento\Framework\Reflection\MethodsMap::class, [ 'cache' => $cacheMock, @@ -48,27 +51,33 @@ protected function setUp() 'fieldNamer' => $fieldNamerMock, ] ); + $this->serializerMock = $this->getMock(SerializerInterface::class); + $objectManager->setBackwardCompatibleProperty( + $this->object, + 'serializer', + $this->serializerMock + ); } public function testGetMethodReturnType() { $this->assertEquals( 'string', - $this->model->getMethodReturnType( + $this->object->getMethodReturnType( \Magento\Framework\Reflection\FieldNamer::class, 'getFieldNameForMethodName' ) ); $this->assertEquals( 'mixed', - $this->model->getMethodReturnType( + $this->object->getMethodReturnType( \Magento\Framework\Reflection\TypeCaster::class, 'castValueToType' ) ); $this->assertEquals( 'array', - $this->model->getMethodReturnType( + $this->object->getMethodReturnType( \Magento\Framework\Reflection\MethodsMap::class, 'getMethodsMap' ) @@ -77,42 +86,46 @@ public function testGetMethodReturnType() public function testGetMethodsMap() { - $methodsMap = $this->model->getMethodsMap(\Magento\Framework\Reflection\MethodsMap::class); - $this->assertEquals( - [ - 'getMethodReturnType' => [ - 'type' => 'string', - 'isRequired' => true, - 'description' => null, - 'parameterCount' => 2, - ], - 'getMethodsMap' => [ - 'type' => 'array', - 'isRequired' => true, - 'description' => "
     Service methods' reflection data stored in cache as 'methodName' => "
    -                        . "'returnType' ex. [ 'create' => '\Magento\Customer\Api\Data\Customer', 'validatePassword' "
    -                        . "=> 'boolean' ] 
    ", - 'parameterCount' => 1, - ], - 'getMethodParams' => [ - 'type' => 'array', - 'isRequired' => true, - 'description' => null, - 'parameterCount' => 2 - ], - 'isMethodValidForDataField' => [ - 'type' => 'bool', - 'isRequired' => true, - 'description' => null, - 'parameterCount' => 2, - ], - 'isMethodReturnValueRequired' => [ - 'type' => 'bool', - 'isRequired' => true, - 'description' => null, - 'parameterCount' => 2, - ], + $data = [ + 'getMethodReturnType' => [ + 'type' => 'string', + 'isRequired' => true, + 'description' => null, + 'parameterCount' => 2, + ], + 'getMethodsMap' => [ + 'type' => 'array', + 'isRequired' => true, + 'description' => "
     Service methods' reflection data stored in cache as 'methodName' => "
    +                    . "'returnType' ex. [ 'create' => '\Magento\Customer\Api\Data\Customer', 'validatePassword' "
    +                    . "=> 'boolean' ] 
    ", + 'parameterCount' => 1, ], + 'getMethodParams' => [ + 'type' => 'array', + 'isRequired' => true, + 'description' => null, + 'parameterCount' => 2 + ], + 'isMethodValidForDataField' => [ + 'type' => 'bool', + 'isRequired' => true, + 'description' => null, + 'parameterCount' => 2, + ], + 'isMethodReturnValueRequired' => [ + 'type' => 'bool', + 'isRequired' => true, + 'description' => null, + 'parameterCount' => 2, + ], + ]; + $this->serializerMock->expects($this->once()) + ->method('serialize') + ->with($data); + $methodsMap = $this->object->getMethodsMap(\Magento\Framework\Reflection\MethodsMap::class); + $this->assertEquals( + $data, $methodsMap ); } @@ -125,7 +138,7 @@ public function testGetMethodsMap() */ public function testIsMethodValidForDataField($type, $methodName, $expectedResult) { - $this->assertEquals($this->model->isMethodValidForDataField($type, $methodName), $expectedResult); + $this->assertEquals($this->object->isMethodValidForDataField($type, $methodName), $expectedResult); } /** @@ -157,7 +170,7 @@ public function isMethodValidForDataFieldProvider() */ public function testIsMethodReturnValueRequired($type, $methodName, $expectedResult) { - $this->assertEquals($this->model->isMethodValidForDataField($type, $methodName), $expectedResult); + $this->assertEquals($this->object->isMethodValidForDataField($type, $methodName), $expectedResult); } /** diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php index 6fa186ee5802e..363031309e334 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php @@ -1,6 +1,6 @@ columns @@ -30,7 +30,7 @@ public function build(RequestBucketInterface $bucket) foreach ($metrics as $metric) { $metricType = $metric->getType(); - if (in_array($metricType, $this->mapMetrics)) { + if (in_array($metricType, $this->allowedMetrics, true)) { $selectAggregations[$metricType] = "$metricType(main_table.value)"; } } diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Range.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Range.php index 2188657bd89ae..9cce6e2dbfadc 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Range.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Range.php @@ -1,6 +1,6 @@ scoreBuilderFactory = $scoreBuilderFactory; $this->filterBuilder = $filterBuilder; @@ -102,6 +116,11 @@ public function __construct( $this->queryContainerFactory = $queryContainerFactory; $this->matchBuilder = $matchBuilder; $this->temporaryStorage = $temporaryStorageFactory->create(); + $this->temporaryStorageFactory = $temporaryStorageFactory; + if (!in_array($relevanceCalculationMethod, ['SUM', 'MAX'], true)) { + throw new \LogicException('Unsupported relevance calculation method used. Only SUM and MAX are allowed'); + } + $this->relevanceCalculationMethod = $relevanceCalculationMethod; } /** @@ -150,9 +169,12 @@ public function buildQuery(RequestInterface $request) } /** + * Creates Select which wraps search result select + * + * It is used to group search results by entity id. + * * @param Select $select * @param ScoreBuilder $scoreBuilder - * @param string $scorePattern * @return Select */ private function createAroundSelect(Select $select, ScoreBuilder $scoreBuilder) @@ -162,7 +184,7 @@ private function createAroundSelect(Select $select, ScoreBuilder $scoreBuilder) ['main_select' => $select], [ $this->entityMetadata->getEntityId() => 'entity_id', - 'relevance' => sprintf('MAX(%s)', $scoreBuilder->getScoreAlias()) + 'relevance' => sprintf('%s(%s)', $this->relevanceCalculationMethod, $scoreBuilder->getScoreAlias()), ] )->group($this->entityMetadata->getEntityId()); return $parentSelect; diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php index a30ff53f9a35a..5bee3b833cf7c 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php @@ -1,6 +1,6 @@ setMethods(['getQuery', 'getIndex', 'getSize']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->request->expects($this->exactly(2)) + $this->request->expects($this->any()) ->method('getIndex') ->will($this->returnValue(self::INDEX_NAME)); @@ -537,4 +537,20 @@ private function createSelectMock(Select $from = null, $isInternal = true, $isGr return $select; } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Unsupported relevance calculation method used. + */ + public function testUnsupportedRelevanceCalculationMethod() + { + $helper = new ObjectManager($this); + $helper->getObject( + \Magento\Framework\Search\Adapter\Mysql\Mapper::class, + [ + 'indexProviders' => [], + 'relevanceCalculationMethod' => 'UNSUPPORTED' + ] + ); + } } diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php index c677e045f6377..2626d68428c34 100644 --- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php +++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/Builder/MatchTest.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/Search/etc/requests.xsd b/lib/internal/Magento/Framework/Search/etc/requests.xsd index f185699c5a5e8..67ce202c7f951 100644 --- a/lib/internal/Magento/Framework/Search/etc/requests.xsd +++ b/lib/internal/Magento/Framework/Search/etc/requests.xsd @@ -1,7 +1,7 @@ @@ -263,6 +263,7 @@ + diff --git a/lib/internal/Magento/Framework/Search/etc/search_engine.xsd b/lib/internal/Magento/Framework/Search/etc/search_engine.xsd index c6dad2d6b3f8c..3ca174a8d49c5 100644 --- a/lib/internal/Magento/Framework/Search/etc/search_engine.xsd +++ b/lib/internal/Magento/Framework/Search/etc/search_engine.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Search/etc/search_request.xsd b/lib/internal/Magento/Framework/Search/etc/search_request.xsd index e4ca02d9f0997..01851b1695d5a 100644 --- a/lib/internal/Magento/Framework/Search/etc/search_request.xsd +++ b/lib/internal/Magento/Framework/Search/etc/search_request.xsd @@ -1,7 +1,7 @@ @@ -25,4 +25,4 @@ - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/Search/etc/search_request_merged.xsd b/lib/internal/Magento/Framework/Search/etc/search_request_merged.xsd index dab110ee5333a..b0243ae7e5b4c 100644 --- a/lib/internal/Magento/Framework/Search/etc/search_request_merged.xsd +++ b/lib/internal/Magento/Framework/Search/etc/search_request_merged.xsd @@ -1,7 +1,7 @@ @@ -32,4 +32,4 @@ - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/Serialize/README.md b/lib/internal/Magento/Framework/Serialize/README.md new file mode 100644 index 0000000000000..5af8fb7f71b6b --- /dev/null +++ b/lib/internal/Magento/Framework/Serialize/README.md @@ -0,0 +1,8 @@ +# Serialize + +**Serialize** library provides interface *SerializerInterface* and multiple implementations: + + * *Json* - default implementation. Uses PHP native json_encode/json_decode functions; + * *Serialize* - less secure than *Json*, but gives higher performance on big arrays. Uses PHP native serialize/unserialize functions, does not unserialize objects on PHP 7. + +Using *Serialize* implementation directly is discouraged, always use *SerializerInterface*, using *Serialize* implementation may lead to security vulnerabilities. \ No newline at end of file diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php new file mode 100644 index 0000000000000..24fadfcb0deee --- /dev/null +++ b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php @@ -0,0 +1,30 @@ +getPhpVersion() >= 7) { + return unserialize($string, ['allowed_classes' => false]); + } + return unserialize($string); + } + + /** + * Return major PHP version + * + * @return int + */ + private function getPhpVersion() + { + return PHP_MAJOR_VERSION; + } +} diff --git a/lib/internal/Magento/Framework/Serialize/SerializerInterface.php b/lib/internal/Magento/Framework/Serialize/SerializerInterface.php new file mode 100644 index 0000000000000..3fc589fab7a8e --- /dev/null +++ b/lib/internal/Magento/Framework/Serialize/SerializerInterface.php @@ -0,0 +1,28 @@ +json = $objectManager->getObject(Json::class); + } + + /** + * @param string|int|float|bool|array|null $value + * @param string $expected + * @dataProvider serializeDataProvider + */ + public function testSerialize($value, $expected) + { + $this->assertEquals( + $expected, + $this->json->serialize($value) + ); + } + + public function serializeDataProvider() + { + $dataObject = new DataObject(['something']); + return [ + ['', '""'], + ['string', '"string"'], + [null, 'null'], + [false, 'false'], + [['a' => 'b', 'd' => 123], '{"a":"b","d":123}'], + [123, '123'], + [10.56, '10.56'], + [$dataObject, '{}'], + ]; + } + + /** + * @param string $value + * @param string|int|float|bool|array|null $expected + * @dataProvider unserializeDataProvider + */ + public function testUnserialize($value, $expected) + { + $this->assertEquals( + $expected, + $this->json->unserialize($value) + ); + } + + public function unserializeDataProvider() + { + return [ + ['""', ''], + ['"string"', 'string'], + ['null', null], + ['false', false], + ['{"a":"b","d":123}', ['a' => 'b', 'd' => 123]], + ['123', 123], + ['10.56', 10.56], + ['{}', []], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/SerializeTest.php b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/SerializeTest.php new file mode 100644 index 0000000000000..5d1e34a542826 --- /dev/null +++ b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/SerializeTest.php @@ -0,0 +1,71 @@ +serialize = $objectManager->getObject(Serialize::class); + } + + /** + * @param string|int|float|bool|array|null $value + * @param string $serializedValue + * @dataProvider serializeDataProvider + */ + public function testSerialize($value, $serializedValue) + { + $this->assertEquals($serializedValue, $this->serialize->serialize($value)); + } + + public function serializeDataProvider() + { + return [ + ['string', 's:6:"string";'], + ['', 's:0:"";'], + [10, 'i:10;'], + [10.5, 'd:10.5;'], + [null, 'N;'], + [false, 'b:0;'], + [['foo' => 'bar'], 'a:1:{s:3:"foo";s:3:"bar";}'], + ]; + } + + /** + * @param string $serializedValue + * @param string|int|float|bool|array|null $value + * @dataProvider unserializeDataProvider + */ + public function testUnserialize($serializedValue, $value) + { + $this->assertEquals($value, $this->serialize->unserialize($serializedValue)); + } + + public function unserializeDataProvider() + { + return [ + ['s:6:"string";', 'string'], + ['s:0:"";', ''], + ['i:10;', 10], + ['d:10.5;', 10.5], + ['N;', null], + ['b:0;', false], + ['a:1:{s:3:"foo";s:3:"bar";}', ['foo' => 'bar']], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Session/Config.php b/lib/internal/Magento/Framework/Session/Config.php index 2e9fa44a794a8..7c4fc08a5db9a 100644 --- a/lib/internal/Magento/Framework/Session/Config.php +++ b/lib/internal/Magento/Framework/Session/Config.php @@ -2,7 +2,7 @@ /** * Session configuration object * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Session; diff --git a/lib/internal/Magento/Framework/Session/Config/ConfigInterface.php b/lib/internal/Magento/Framework/Session/Config/ConfigInterface.php index f4928f392a71f..6616796dfd9f5 100644 --- a/lib/internal/Magento/Framework/Session/Config/ConfigInterface.php +++ b/lib/internal/Magento/Framework/Session/Config/ConfigInterface.php @@ -2,7 +2,7 @@ /** * Session config interface * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Session\Config; diff --git a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php index 265ef90d0873b..80dd72b5a66ff 100644 --- a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php +++ b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php @@ -1,6 +1,6 @@ expireSessionCookie(); diff --git a/lib/internal/Magento/Framework/Session/SessionManagerInterface.php b/lib/internal/Magento/Framework/Session/SessionManagerInterface.php index ab6dbb225a8ff..f641027ef186a 100644 --- a/lib/internal/Magento/Framework/Session/SessionManagerInterface.php +++ b/lib/internal/Magento/Framework/Session/SessionManagerInterface.php @@ -2,7 +2,7 @@ /** * Magento session manager interface * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Session; diff --git a/lib/internal/Magento/Framework/Session/SidResolver.php b/lib/internal/Magento/Framework/Session/SidResolver.php index e8006d644e48c..1bf6282405be6 100644 --- a/lib/internal/Magento/Framework/Session/SidResolver.php +++ b/lib/internal/Magento/Framework/Session/SidResolver.php @@ -2,7 +2,7 @@ /** * SID resolver * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Session; diff --git a/lib/internal/Magento/Framework/Session/SidResolverInterface.php b/lib/internal/Magento/Framework/Session/SidResolverInterface.php index d60575e59fe21..8b51198b17de9 100644 --- a/lib/internal/Magento/Framework/Session/SidResolverInterface.php +++ b/lib/internal/Magento/Framework/Session/SidResolverInterface.php @@ -2,7 +2,7 @@ /** * SID resolver interface * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Session; diff --git a/lib/internal/Magento/Framework/Session/Storage.php b/lib/internal/Magento/Framework/Session/Storage.php index aed4c88b76936..eda48b74e2854 100644 --- a/lib/internal/Magento/Framework/Session/Storage.php +++ b/lib/internal/Magento/Framework/Session/Storage.php @@ -2,7 +2,7 @@ /** * Default session storage * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Session; diff --git a/lib/internal/Magento/Framework/Session/StorageInterface.php b/lib/internal/Magento/Framework/Session/StorageInterface.php index a842f14860b94..30d54cc151b0b 100644 --- a/lib/internal/Magento/Framework/Session/StorageInterface.php +++ b/lib/internal/Magento/Framework/Session/StorageInterface.php @@ -2,7 +2,7 @@ /** * Session storage interface * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Session; diff --git a/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php b/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php index 1df7cfa69ca83..183aaabc12fbc 100644 --- a/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php +++ b/lib/internal/Magento/Framework/Session/Test/Unit/ConfigTest.php @@ -1,6 +1,6 @@ installationWritableDirectories[$code] = $this->directoryList->getPath($code); @@ -130,7 +134,7 @@ public function getInstallationCurrentWritableDirectories() } /** - * Check all sub-directories and files except for var/generation and var/di + * Check all sub-directories and files except for generated/code and generated/metadata * * @param string $directory * @return bool @@ -142,8 +146,8 @@ private function checkRecursiveDirectories($directory) \RecursiveIteratorIterator::CHILD_FIRST ); $noWritableFilesFolders = [ - $this->directoryList->getPath(DirectoryList::GENERATION) . '/', - $this->directoryList->getPath(DirectoryList::DI) . '/', + $this->directoryList->getPath(DirectoryList::GENERATED_CODE) . '/', + $this->directoryList->getPath(DirectoryList::GENERATED_METADATA) . '/', ]; $directoryIterator = new Filter($directoryIterator, $noWritableFilesFolders); diff --git a/lib/internal/Magento/Framework/Setup/InstallDataInterface.php b/lib/internal/Magento/Framework/Setup/InstallDataInterface.php index 24f7a18ae6986..e3f4ccf2cf375 100644 --- a/lib/internal/Magento/Framework/Setup/InstallDataInterface.php +++ b/lib/internal/Magento/Framework/Setup/InstallDataInterface.php @@ -1,6 +1,6 @@ assertEquals($expected, $this->filePermissions->getInstallationWritableDirectories()); @@ -157,6 +158,7 @@ public function testGetMissingWritableDirectoriesAndPathsForInstallation() BP . '/var', BP . '/pub/media', BP . '/pub/static', + BP . '/generated' ]; $this->assertEquals( @@ -231,6 +233,11 @@ public function setUpDirectoryListInstallation() ->method('getPath') ->with(DirectoryList::STATIC_VIEW) ->will($this->returnValue(BP . '/pub/static')); + $this->directoryListMock + ->expects($this->at(4)) + ->method('getPath') + ->with(DirectoryList::GENERATED) + ->will($this->returnValue(BP . '/generated')); } public function setUpDirectoryWriteInstallation() diff --git a/lib/internal/Magento/Framework/Setup/Test/Unit/ListsTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/ListsTest.php index 4ce704c3b6adf..a3728abbfbc2a 100644 --- a/lib/internal/Magento/Framework/Setup/Test/Unit/ListsTest.php +++ b/lib/internal/Magento/Framework/Setup/Test/Unit/ListsTest.php @@ -1,6 +1,6 @@ @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/Simplexml/Test/Unit/_files/extend_data.xml b/lib/internal/Magento/Framework/Simplexml/Test/Unit/_files/extend_data.xml index c070901c5d978..fc75234d3b471 100644 --- a/lib/internal/Magento/Framework/Simplexml/Test/Unit/_files/extend_data.xml +++ b/lib/internal/Magento/Framework/Simplexml/Test/Unit/_files/extend_data.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Simplexml/Test/Unit/_files/mixed_data.xml b/lib/internal/Magento/Framework/Simplexml/Test/Unit/_files/mixed_data.xml index 193d0c752369e..34356fdd6dbac 100644 --- a/lib/internal/Magento/Framework/Simplexml/Test/Unit/_files/mixed_data.xml +++ b/lib/internal/Magento/Framework/Simplexml/Test/Unit/_files/mixed_data.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Stdlib/ArrayManager.php b/lib/internal/Magento/Framework/Stdlib/ArrayManager.php index f8b758bb9d9df..2ee1a23ce8380 100644 --- a/lib/internal/Magento/Framework/Stdlib/ArrayManager.php +++ b/lib/internal/Magento/Framework/Stdlib/ArrayManager.php @@ -1,6 +1,6 @@ ['existing' => ['path' => 1]], 'value' => 'valuable data', 'result' => ['existing' => ['path' => 1], 'new' => ['path' => [2 => 'valuable data']]] + ], + 3 => [ + 'path' => ['new', 'path/2'], + 'data' => ['existing' => ['path' => 1]], + 'value' => 'valuable data', + 'result' => ['existing' => ['path' => 1], 'new' => ['path' => [2 => 'valuable data']]] ] ]; } @@ -178,6 +184,12 @@ public function setReplaceProvider() 'data' => ['existing' => ['path' => 1]], 'value' => 'valuable data', 'result' => ['existing' => ['path' => 1]] + ], + 3 => [ + 'path' => ['new', 'path', '2'], + 'data' => ['existing' => ['path' => 1]], + 'value' => 'valuable data', + 'result' => ['existing' => ['path' => 1]] ] ]; } @@ -228,6 +240,13 @@ public function moveDataProvider() 'data' => ['valid' => ['path' => 'value'], 'target' => ['path' => 'exists']], 'overwrite' => true, 'result' => ['valid' => [], 'target' => ['path' => 'value']] + ], + 4 => [ + 'path' => ['valid', 'path'], + 'targetPath' => 'target/path', + 'data' => ['valid' => ['path' => 'value'], 'target' => ['path' => 'exists']], + 'overwrite' => true, + 'result' => ['valid' => [], 'target' => ['path' => 'value']] ] ]; } @@ -267,7 +286,13 @@ public function mergeDataProvider() 'data' => [], 'value' => [true], 'result' => [] - ] + ], + 3 => [ + 'path' => ['0', 'path/1'], + 'data' => [['path' => [false, ['value' => false]]]], + 'value' => ['value' => true, 'new_value' => false], + 'result' => [['path' => [false, ['value' => true, 'new_value' => false]]]] + ], ]; } @@ -337,7 +362,12 @@ public function removeDataProvider() 'path' => 'invalid', 'data' => [true], 'result' => [true] - ] + ], + 3 => [ + 'path' => ['simple'], + 'data' => ['simple' => true, 'complex' => false], + 'result' => ['complex' => false] + ], ]; } @@ -550,7 +580,7 @@ public function slicePathDataProvider() 'offset' => -6, 'length' => 3, 'result' => 'path/0/goes' - ] + ], ]; } diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/ArrayUtilsTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/ArrayUtilsTest.php index d9786ff071c7e..cf8f821cc8684 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/ArrayUtilsTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/ArrayUtilsTest.php @@ -1,6 +1,6 @@ batchSize = 10; + $this->currentBatch = 0; + $this->correlationName = 'correlationName'; + $this->rangeField = 'rangeField'; + $this->rangeFieldAlias = 'rangeFieldAlias'; + + $this->selectMock = $this->getMock(Select::class, [], [], '', false, false); + $this->wrapperSelectMock = $this->getMock(Select::class, [], [], '', false, false); + $this->connectionMock = $this->getMock(AdapterInterface::class); + $this->connectionMock->expects($this->any())->method('select')->willReturn($this->wrapperSelectMock); + $this->selectMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); + $this->connectionMock->expects($this->any())->method('quoteIdentifier')->willReturnArgument(0); + + $this->model = new BatchRangeIterator( + $this->selectMock, + $this->batchSize, + $this->correlationName, + $this->rangeField, + $this->rangeFieldAlias + ); + } + + /** + * Test steps: + * 1. $iterator->current(); + * 2. $iterator->key(); + * @return void + */ + public function testCurrent() + { + $this->selectMock->expects($this->once())->method('limit')->with($this->currentBatch, $this->batchSize); + $this->selectMock->expects($this->once())->method('order')->with('correlationName.rangeField' . ' ASC'); + $this->assertEquals($this->selectMock, $this->model->current()); + $this->assertEquals(0, $this->model->key()); + } + + /** + * Test the separation of batches + */ + public function testIterations() + { + $iterations = 0; + + $this->connectionMock->expects($this->once()) + ->method('fetchRow') + ->willReturn(['cnt' => 105]); + + foreach ($this->model as $key) { + $this->assertEquals($this->selectMock, $key); + $iterations++; + }; + + $this->assertEquals(10, $iterations); + } +} diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php index 4be22eb14ca85..28e06506f66ce 100644 --- a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php @@ -1,12 +1,13 @@ factoryMock = $this->getMock(BatchIteratorFactory::class, [], [], '', false, false); + $this->rangeFactoryMock = $this->getMock(BatchRangeIteratorFactory::class, ['create'], [], '', false, false); $this->selectMock = $this->getMock(Select::class, [], [], '', false, false); $this->iteratorMock = $this->getMock(BatchIterator::class, [], [], '', false, false); - $this->model = new Generator($this->factoryMock); + $this->model = new Generator($this->factoryMock, $this->rangeFactoryMock); } /** @@ -165,4 +172,40 @@ public function testGenerateWithInvalidWithWildcard() )->willReturn($this->iteratorMock); $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); } + + /** + * Test success generate with non-unique strategy. + * @return void + */ + public function testGenerateWithNonUniqueStrategy() + { + $map = [ + [ + Select::FROM, + [ + 'cp' => ['joinType' => Select::FROM] + ] + ], + [ + Select::COLUMNS, + [ + ['cp', 'entity_id', 'product_id'] + ] + ] + ]; + $this->selectMock->expects($this->exactly(2))->method('getPart')->willReturnMap($map); + $this->factoryMock->expects($this->once())->method('create')->with( + [ + 'select' => $this->selectMock, + 'batchSize' => 100, + 'correlationName' => 'cp', + 'rangeField' => 'entity_id', + 'rangeFieldAlias' => 'product_id' + ] + )->willReturn($this->iteratorMock); + $this->assertEquals( + $this->iteratorMock, + $this->model->generate('entity_id', $this->selectMock, 100, 'non_unique') + ); + } } diff --git a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php index 2d15510baebd9..75e6e7892f57f 100644 --- a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php @@ -1,6 +1,6 @@ 'synchronize']; @@ -71,13 +81,22 @@ protected function createInstance(array $data = []) $resourceCollection = $this->getMockBuilder(\Magento\Framework\Data\Collection\AbstractDb::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->json = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->setMethods(null) + ->getMock(); + + $this->serialize = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Serialize::class) + ->setMethods(null) + ->getMock(); $this->flag = new \Magento\Framework\Flag( $context, $registry, $resource, $resourceCollection, - $data + $data, + $this->json, + $this->serialize ); } @@ -94,7 +113,17 @@ public function testConstruct() $this->assertEquals($flagCode, $this->flag->getFlagCode()); } - public function testGetFlagData() + public function testGetFlagDataJson() + { + $result = $this->flag->getFlagData(); + $this->assertNull($result); + $flagData = json_encode('data'); + $this->flag->setData('flag_data', $flagData); + $result = $this->flag->getFlagData(); + $this->assertEquals(json_decode($flagData), $result); + } + + public function testGetFlagDataSerialized() { $result = $this->flag->getFlagData(); $this->assertNull($result); @@ -108,7 +137,7 @@ public function testSetFlagData() { $flagData = 'data'; $this->flag->setFlagData($flagData); - $result = unserialize($this->flag->getData('flag_data')); + $result = json_decode($this->flag->getData('flag_data')); $this->assertEquals($flagData, $result); } diff --git a/lib/internal/Magento/Framework/Test/Unit/Interception/InterceptorTest.php b/lib/internal/Magento/Framework/Test/Unit/Interception/InterceptorTest.php index 7b3624c501d8d..0da6ef45e3b2a 100644 --- a/lib/internal/Magento/Framework/Test/Unit/Interception/InterceptorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/Interception/InterceptorTest.php @@ -1,6 +1,6 @@ viewDesign = $this->getMock(\Magento\Framework\View\DesignInterface::class, [], [], '', false); $this->cache = $this->getMock(\Magento\Framework\Cache\FrontendInterface::class, [], [], '', false); $this->viewFileSystem = $this->getMock(\Magento\Framework\View\FileSystem::class, [], [], '', false); @@ -104,6 +106,21 @@ protected function setUp() $this->csvParser, $this->packDictionary ); + + $serializerMock = $this->getMock(SerializerInterface::class); + $serializerMock->method('serialize') + ->willReturnCallback(function ($data) { + return json_encode($data); + }); + $serializerMock->method('unserialize') + ->willReturnCallback(function ($string) { + return json_decode($string, true); + }); + $objectManager->setBackwardCompatibleProperty( + $this->translate, + 'serializer', + $serializerMock + ); } /** @@ -119,7 +136,7 @@ public function testLoadData($area, $forceReload, $cachedData) $this->cache->expects($this->exactly($forceReload ? 0 : 1)) ->method('load') - ->will($this->returnValue(serialize($cachedData))); + ->will($this->returnValue(json_encode($cachedData))); if (!$forceReload && $cachedData !== false) { $this->translate->loadData($area, $forceReload); @@ -222,7 +239,7 @@ public function testGetData($data, $result) { $this->cache->expects($this->once()) ->method('load') - ->will($this->returnValue(serialize($data))); + ->will($this->returnValue(json_encode($data))); $this->expectsSetConfig('themeId'); $this->translate->loadData('frontend'); $this->assertEquals($result, $this->translate->getData()); diff --git a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php index 36fce179395b5..cfb4fb884accb 100644 --- a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php @@ -1,12 +1,13 @@ routeParamsResolverMock = $this->getMock( @@ -549,18 +555,17 @@ public function testAddSessionParam() /** * @param bool $result - * @param string $baseUrl * @param string $referrer * @dataProvider isOwnOriginUrlDataProvider */ - public function testIsOwnOriginUrl($result, $baseUrl, $referrer) + public function testIsOwnOriginUrl($result, $referrer) { $requestMock = $this->getRequestMock(); - $model = $this->getUrlModel(['scopeResolver' => $this->scopeResolverMock, 'request' => $requestMock]); + $this->hostChecker = $this->getMockBuilder(HostChecker::class) + ->disableOriginalConstructor()->getMock(); + $this->hostChecker->expects($this->once())->method('isOwnOrigin')->with($referrer)->willReturn($result); + $model = $this->getUrlModel(['hostChecker' => $this->hostChecker, 'request' => $requestMock]); - $this->scopeMock->expects($this->any())->method('getBaseUrl')->will($this->returnValue($baseUrl)); - $this->scopeResolverMock->expects($this->any())->method('getScopes') - ->will($this->returnValue([$this->scopeMock])); $requestMock->expects($this->once())->method('getServer')->with('HTTP_REFERER') ->will($this->returnValue($referrer)); @@ -570,8 +575,8 @@ public function testIsOwnOriginUrl($result, $baseUrl, $referrer) public function isOwnOriginUrlDataProvider() { return [ - 'is origin url' => [true, 'http://localhost/', 'http://localhost/'], - 'is not origin url' => [false, 'http://localhost/', 'http://example.com/'], + 'is origin url' => [true, 'http://localhost/'], + 'is not origin url' => [false, 'http://example.com/'], ]; } diff --git a/lib/internal/Magento/Framework/Test/Unit/UtilTest.php b/lib/internal/Magento/Framework/Test/Unit/UtilTest.php index 8ca76e375b8b7..a356349d85684 100644 --- a/lib/internal/Magento/Framework/Test/Unit/UtilTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/UtilTest.php @@ -2,7 +2,7 @@ /** * Collection of various useful functions * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Test\Unit; diff --git a/lib/internal/Magento/Framework/Test/Unit/ValidatorFactoryTest.php b/lib/internal/Magento/Framework/Test/Unit/ValidatorFactoryTest.php index 2c8dca01988e7..9474e72651fce 100644 --- a/lib/internal/Magento/Framework/Test/Unit/ValidatorFactoryTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/ValidatorFactoryTest.php @@ -2,7 +2,7 @@ /** * Unit test for Magento\Framework\ValidatorFactory * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/lib/internal/Magento/Framework/Test/Unit/ValidatorTest.php b/lib/internal/Magento/Framework/Test/Unit/ValidatorTest.php index af48d24de887d..6745f4e6c0544 100644 --- a/lib/internal/Magento/Framework/Test/Unit/ValidatorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/ValidatorTest.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/TestFramework/Test/Unit/Unit/Utility/_files/valid.xml b/lib/internal/Magento/Framework/TestFramework/Test/Unit/Unit/Utility/_files/valid.xml index 8eee79f23b73e..2afb2f8379e9b 100644 --- a/lib/internal/Magento/Framework/TestFramework/Test/Unit/Unit/Utility/_files/valid.xml +++ b/lib/internal/Magento/Framework/TestFramework/Test/Unit/Unit/Utility/_files/valid.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/TestFramework/Test/Unit/Unit/Utility/_files/valid.xsd b/lib/internal/Magento/Framework/TestFramework/Test/Unit/Unit/Utility/_files/valid.xsd index aec264cdb4173..4640abfcf07e8 100644 --- a/lib/internal/Magento/Framework/TestFramework/Test/Unit/Unit/Utility/_files/valid.xsd +++ b/lib/internal/Magento/Framework/TestFramework/Test/Unit/Unit/Utility/_files/valid.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/AbstractFactoryTestCase.php b/lib/internal/Magento/Framework/TestFramework/Unit/AbstractFactoryTestCase.php index 1efff6d6761dd..87c0f48eb7c82 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/AbstractFactoryTestCase.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/AbstractFactoryTestCase.php @@ -2,7 +2,7 @@ /** * Framework for unit tests containing helper methods * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. * * Number of fields is necessary because of the number of fields used by multiple layers diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/ExtensionGeneratorAutoloader.php b/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/ExtensionGeneratorAutoloader.php index 4e0fcac1fdff2..9f8cb85b76ecd 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/ExtensionGeneratorAutoloader.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/ExtensionGeneratorAutoloader.php @@ -1,6 +1,6 @@ getProperty($propertyName); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($object, $propertyValue); diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ProxyTesting.php b/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ProxyTesting.php index 85d21ed2d1a75..077dc7e2bea6f 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ProxyTesting.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ProxyTesting.php @@ -1,6 +1,6 @@ _cache->load($this->getCacheId()); if ($data) { - $data = unserialize($data); + $data = $this->getSerializer()->unserialize($data); } return $data; } @@ -486,7 +491,22 @@ protected function _loadCache() */ protected function _saveCache() { - $this->_cache->save(serialize($this->getData()), $this->getCacheId(true), [], false); + $this->_cache->save($this->getSerializer()->serialize($this->getData()), $this->getCacheId(true), [], false); return $this; } + + /** + * Get serializer + * + * @return \Magento\Framework\Serialize\SerializerInterface + * @deprecated + */ + private function getSerializer() + { + if ($this->serializer === null) { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() + ->get(Serialize\SerializerInterface::class); + } + return $this->serializer; + } } diff --git a/lib/internal/Magento/Framework/Translate/AbstractAdapter.php b/lib/internal/Magento/Framework/Translate/AbstractAdapter.php index 2f0ceb7735eae..cd43d61074f52 100644 --- a/lib/internal/Magento/Framework/Translate/AbstractAdapter.php +++ b/lib/internal/Magento/Framework/Translate/AbstractAdapter.php @@ -1,6 +1,6 @@ _request = $request; $this->_routeConfig = $routeConfig; @@ -218,6 +227,8 @@ public function __construct( $this->_scopeConfig = $scopeConfig; $this->routeParamsPreprocessor = $routeParamsPreprocessor; $this->_scopeType = $scopeType; + $this->hostChecker = $hostChecker ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(HostChecker::class); parent::__construct($data); } @@ -1086,17 +1097,7 @@ public function useSessionIdForUrl($secure = false) */ public function isOwnOriginUrl() { - $scopeDomains = []; - $referer = parse_url($this->_request->getServer('HTTP_REFERER'), PHP_URL_HOST); - foreach ($this->_scopeResolver->getScopes() as $scope) { - $scopeDomains[] = parse_url($scope->getBaseUrl(), PHP_URL_HOST); - $scopeDomains[] = parse_url($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, true), PHP_URL_HOST); - } - $scopeDomains = array_unique($scopeDomains); - if (empty($referer) || in_array($referer, $scopeDomains)) { - return true; - } - return false; + return $this->hostChecker->isOwnOrigin($this->_request->getServer('HTTP_REFERER')); } /** @@ -1163,7 +1164,7 @@ protected function getRouteParamsResolver() private function getUrlModifier() { if ($this->urlModifier === null) { - $this->urlModifier = \Magento\Framework\App\ObjectManager::getInstance()->get( + $this->urlModifier = \Magento\Framework\App\ObjectManager::getInstance()->get( \Magento\Framework\Url\ModifierInterface::class ); } diff --git a/lib/internal/Magento/Framework/Url/Decoder.php b/lib/internal/Magento/Framework/Url/Decoder.php index d60611062d076..06a865db0079f 100644 --- a/lib/internal/Magento/Framework/Url/Decoder.php +++ b/lib/internal/Magento/Framework/Url/Decoder.php @@ -1,6 +1,6 @@ scopeResolver = $scopeResolver; + } + + /** + * Check if provided URL is one of the domain URLs assigned to scopes + * + * @param string $url + * @return bool + */ + public function isOwnOrigin($url) + { + $scopeHostNames = []; + $hostName = parse_url($url, PHP_URL_HOST); + if (empty($hostName)) { + return true; + } + foreach ($this->scopeResolver->getScopes() as $scope) { + $scopeHostNames[] = parse_url($scope->getBaseUrl(), PHP_URL_HOST); + $scopeHostNames[] = parse_url($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, true), PHP_URL_HOST); + } + $scopeHostNames = array_unique($scopeHostNames); + return in_array($hostName, $scopeHostNames); + } +} diff --git a/lib/internal/Magento/Framework/Url/ModifierComposite.php b/lib/internal/Magento/Framework/Url/ModifierComposite.php index 044faf23182de..1f301f4ae5e83 100644 --- a/lib/internal/Magento/Framework/Url/ModifierComposite.php +++ b/lib/internal/Magento/Framework/Url/ModifierComposite.php @@ -1,6 +1,6 @@ scopeResolver = $this->getMockBuilder( + \Magento\Framework\Url\ScopeResolverInterface::class + )->getMock(); + + $objectManager = new ObjectManager($this); + $this->object = $objectManager->getObject( + \Magento\Framework\Url\HostChecker::class, + [ + 'scopeResolver' => $this->scopeResolver + ] + ); + } + + /** + * @dataProvider isOwnOriginDataProvider + * @param string $url + * @param boolean $result + */ + public function testIsOwnOrigin($url, $result) + { + $scopes[0] = $this->getMockBuilder(\Magento\Framework\Url\ScopeInterface::class)->getMock(); + $scopes[0]->expects($this->any())->method('getBaseUrl')->willReturn('http://www.example.com'); + $scopes[1] = $this->getMockBuilder(\Magento\Framework\Url\ScopeInterface::class)->getMock(); + $scopes[1]->expects($this->any())->method('getBaseUrl')->willReturn('https://www.example2.com'); + + $this->scopeResolver->expects($this->atLeastOnce())->method('getScopes')->willReturn($scopes); + + $this->assertEquals($result, $this->object->isOwnOrigin($url)); + } + + /** + * @return array + */ + public function isOwnOriginDataProvider() + { + return [ + ['http://www.example.com/some/page/', true], + ['http://www.test.com/other/page/', false], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Url/Test/Unit/QueryParamsResolverTest.php b/lib/internal/Magento/Framework/Url/Test/Unit/QueryParamsResolverTest.php index ea94ea270eb17..ca53f9ef2273a 100644 --- a/lib/internal/Magento/Framework/Url/Test/Unit/QueryParamsResolverTest.php +++ b/lib/internal/Magento/Framework/Url/Test/Unit/QueryParamsResolverTest.php @@ -1,6 +1,6 @@ listOfProtocols = $listOfProtocols; + } + } + + /** + * Validate URI + * + * @param string $value + * @return bool + */ + public function isValid($value) + { + $uri = new Uri($value); + $isValid = in_array( + strtolower($uri->getScheme()), + $this->listOfProtocols + ); + if (!$isValid) { + $this->_addMessages(["Protocol isn't allowed"]); + } + return $isValid; + } +} diff --git a/lib/internal/Magento/Framework/Validator/Alnum.php b/lib/internal/Magento/Framework/Validator/Alnum.php index da0bc22ebc708..bc3470b54c3db 100644 --- a/lib/internal/Magento/Framework/Validator/Alnum.php +++ b/lib/internal/Magento/Framework/Validator/Alnum.php @@ -2,7 +2,7 @@ /** * Alphanumerical validator * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator; diff --git a/lib/internal/Magento/Framework/Validator/Builder.php b/lib/internal/Magento/Framework/Validator/Builder.php index e31a2c86d9238..fe5465124d878 100644 --- a/lib/internal/Magento/Framework/Validator/Builder.php +++ b/lib/internal/Magento/Framework/Validator/Builder.php @@ -2,7 +2,7 @@ /** * Magento Validator Builder * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/lib/internal/Magento/Framework/Validator/Config.php b/lib/internal/Magento/Framework/Validator/Config.php index dc0d6a6259db2..7860fc811220c 100644 --- a/lib/internal/Magento/Framework/Validator/Config.php +++ b/lib/internal/Magento/Framework/Validator/Config.php @@ -2,7 +2,7 @@ /** * Validation configuration files handler * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator; diff --git a/lib/internal/Magento/Framework/Validator/Constraint.php b/lib/internal/Magento/Framework/Validator/Constraint.php index 3e40085397121..af87ba8e9029d 100644 --- a/lib/internal/Magento/Framework/Validator/Constraint.php +++ b/lib/internal/Magento/Framework/Validator/Constraint.php @@ -2,7 +2,7 @@ /** * Validator constraint delegates validation to wrapped validator. * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator; diff --git a/lib/internal/Magento/Framework/Validator/Constraint/Option.php b/lib/internal/Magento/Framework/Validator/Constraint/Option.php index 2ce72ca0c62a2..55988c73c3c79 100644 --- a/lib/internal/Magento/Framework/Validator/Constraint/Option.php +++ b/lib/internal/Magento/Framework/Validator/Constraint/Option.php @@ -2,7 +2,7 @@ /** * Constraint option * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator\Constraint; diff --git a/lib/internal/Magento/Framework/Validator/Constraint/Option/Callback.php b/lib/internal/Magento/Framework/Validator/Constraint/Option/Callback.php index 201aac589647a..4b704902af897 100644 --- a/lib/internal/Magento/Framework/Validator/Constraint/Option/Callback.php +++ b/lib/internal/Magento/Framework/Validator/Constraint/Option/Callback.php @@ -2,7 +2,7 @@ /** * Constraint callback option * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator\Constraint\Option; diff --git a/lib/internal/Magento/Framework/Validator/Constraint/OptionInterface.php b/lib/internal/Magento/Framework/Validator/Constraint/OptionInterface.php index 2b63986c82e53..1c94a804b579a 100644 --- a/lib/internal/Magento/Framework/Validator/Constraint/OptionInterface.php +++ b/lib/internal/Magento/Framework/Validator/Constraint/OptionInterface.php @@ -2,7 +2,7 @@ /** * Validator Constraint Option interface * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator\Constraint; diff --git a/lib/internal/Magento/Framework/Validator/Constraint/Property.php b/lib/internal/Magento/Framework/Validator/Constraint/Property.php index c40e3c1237e64..8178445796f50 100644 --- a/lib/internal/Magento/Framework/Validator/Constraint/Property.php +++ b/lib/internal/Magento/Framework/Validator/Constraint/Property.php @@ -2,7 +2,7 @@ /** * Validator constraint delegates validation of value's property to wrapped validator. * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/lib/internal/Magento/Framework/Validator/ConstraintFactory.php b/lib/internal/Magento/Framework/Validator/ConstraintFactory.php index 1077f21bfa2bd..dd56b49efc5dd 100644 --- a/lib/internal/Magento/Framework/Validator/ConstraintFactory.php +++ b/lib/internal/Magento/Framework/Validator/ConstraintFactory.php @@ -1,6 +1,6 @@ _configFiles = $this->cache->load(self::CACHE_KEY); if (!$this->_configFiles) { $this->_configFiles = $this->moduleReader->getConfigurationFiles('validation.xml'); - $this->cache->save(serialize($this->_configFiles), self::CACHE_KEY); + $this->cache->save($this->getSerializer()->serialize($this->_configFiles->toArray()), self::CACHE_KEY); } else { - $this->_configFiles = unserialize($this->_configFiles); + $filesArray = $this->getSerializer()->unserialize($this->_configFiles); + $this->_configFiles = $this->getFileIteratorFactory()->create(array_keys($filesArray)); } } } @@ -109,7 +120,7 @@ public function getValidatorConfig() { $this->_initializeConfigList(); $this->_initializeDefaultTranslator(); - return $this->_objectManager->create( + return $this->_objectManager->create( \Magento\Framework\Validator\Config::class, ['configFiles' => $this->_configFiles]); } @@ -140,4 +151,33 @@ public function createValidator($entityName, $groupName, array $builderConfig = $this->_initializeDefaultTranslator(); return $this->getValidatorConfig()->createValidator($entityName, $groupName, $builderConfig); } + + /** + * Get serializer + * + * @return \Magento\Framework\Serialize\SerializerInterface + * @deprecated + */ + private function getSerializer() + { + if ($this->serializer === null) { + $this->serializer = $this->_objectManager->get(\Magento\Framework\Serialize\SerializerInterface::class); + } + return $this->serializer; + } + + /** + * Get file iterator factory + * + * @return \Magento\Framework\Config\FileIteratorFactory + * @deprecated + */ + private function getFileIteratorFactory() + { + if ($this->fileIteratorFactory === null) { + $this->fileIteratorFactory = $this->_objectManager + ->get(\Magento\Framework\Config\FileIteratorFactory::class); + } + return $this->fileIteratorFactory; + } } diff --git a/lib/internal/Magento/Framework/Validator/File/Extension.php b/lib/internal/Magento/Framework/Validator/File/Extension.php index dfc81aa1fd751..727cfbbf3ebfc 100644 --- a/lib/internal/Magento/Framework/Validator/File/Extension.php +++ b/lib/internal/Magento/Framework/Validator/File/Extension.php @@ -2,7 +2,7 @@ /** * Validator for the file extension of a file * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator\File; diff --git a/lib/internal/Magento/Framework/Validator/File/ImageSize.php b/lib/internal/Magento/Framework/Validator/File/ImageSize.php index 957190a3b5f27..e81a4277400eb 100644 --- a/lib/internal/Magento/Framework/Validator/File/ImageSize.php +++ b/lib/internal/Magento/Framework/Validator/File/ImageSize.php @@ -2,7 +2,7 @@ /** * Validator for the image size of a image file * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator\File; diff --git a/lib/internal/Magento/Framework/Validator/File/IsImage.php b/lib/internal/Magento/Framework/Validator/File/IsImage.php index 8af98b562a62b..f1c829bbe26bf 100644 --- a/lib/internal/Magento/Framework/Validator/File/IsImage.php +++ b/lib/internal/Magento/Framework/Validator/File/IsImage.php @@ -2,7 +2,7 @@ /** * Validator which checks if the file is image * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator\File; diff --git a/lib/internal/Magento/Framework/Validator/File/Size.php b/lib/internal/Magento/Framework/Validator/File/Size.php index a4a058a35294f..e438e9b892b5a 100644 --- a/lib/internal/Magento/Framework/Validator/File/Size.php +++ b/lib/internal/Magento/Framework/Validator/File/Size.php @@ -2,7 +2,7 @@ /** * Validator for the maximum size of a file up to a max of 2GB * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator\File; diff --git a/lib/internal/Magento/Framework/Validator/FloatUtils.php b/lib/internal/Magento/Framework/Validator/FloatUtils.php index 94b151ce7c887..4588731f0f48e 100644 --- a/lib/internal/Magento/Framework/Validator/FloatUtils.php +++ b/lib/internal/Magento/Framework/Validator/FloatUtils.php @@ -2,7 +2,7 @@ /** * Float validator * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator; diff --git a/lib/internal/Magento/Framework/Validator/IntUtils.php b/lib/internal/Magento/Framework/Validator/IntUtils.php index b37b706686d81..2a821c8652233 100644 --- a/lib/internal/Magento/Framework/Validator/IntUtils.php +++ b/lib/internal/Magento/Framework/Validator/IntUtils.php @@ -2,7 +2,7 @@ /** * Integer validator * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator; diff --git a/lib/internal/Magento/Framework/Validator/Ip.php b/lib/internal/Magento/Framework/Validator/Ip.php index d01b6adcc956c..820960b9173e4 100644 --- a/lib/internal/Magento/Framework/Validator/Ip.php +++ b/lib/internal/Magento/Framework/Validator/Ip.php @@ -2,7 +2,7 @@ /** * Ip validator * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Validator; diff --git a/lib/internal/Magento/Framework/Validator/Locale.php b/lib/internal/Magento/Framework/Validator/Locale.php index 2717a97a47fe5..f38b3db3ece1c 100644 --- a/lib/internal/Magento/Framework/Validator/Locale.php +++ b/lib/internal/Magento/Framework/Validator/Locale.php @@ -1,6 +1,6 @@ _defaultTranslator = \Magento\Framework\Validator\AbstractValidator::getDefaultTranslator(); - $this->_objectManager = $this->getMock(\Magento\Framework\ObjectManagerInterface::class); - $this->_validatorConfig = $this->getMockBuilder( - \Magento\Framework\Validator\Config::class - )->setMethods( - ['createValidatorBuilder', 'createValidator'] - )->disableOriginalConstructor()->getMock(); - - $this->_objectManager->expects( - $this->at(0) - )->method( - 'create' - )->with( - \Magento\Framework\Translate\Adapter::class - )->will( - $this->returnValue(new \Magento\Framework\Translate\Adapter()) - ); + $this->defaultTranslator = \Magento\Framework\Validator\AbstractValidator::getDefaultTranslator(); - $this->_objectManager->expects( - $this->at(1) - )->method( - 'create' - )->with( + $this->objectManagerMock = $this->getMock(\Magento\Framework\ObjectManagerInterface::class); + $this->validatorConfigMock = $this->getMock( \Magento\Framework\Validator\Config::class, - ['configFiles' => ['/tmp/moduleOne/etc/validation.xml']] - )->will( - $this->returnValue($this->_validatorConfig) + ['createValidatorBuilder', 'createValidator'], + [], + '', + false ); - - // Config mock - $this->_config = $this->getMockBuilder( - \Magento\Framework\Module\Dir\Reader::class - )->setMethods( - ['getConfigurationFiles'] - )->disableOriginalConstructor()->getMock(); - $this->_config->expects( - $this->once() - )->method( - 'getConfigurationFiles' - )->with( - 'validation.xml' - )->will( - $this->returnValue(['/tmp/moduleOne/etc/validation.xml']) + $translateAdapterMock = $this->getMock(\Magento\Framework\Translate\Adapter::class, [], [], '', false); + $this->objectManagerMock->expects($this->at(0)) + ->method('create') + ->with(\Magento\Framework\Translate\Adapter::class) + ->willReturn($translateAdapterMock); + $this->fileIteratorMock = $this->getMock( + \Magento\Framework\Config\FileIterator::class, + [], + [], + '', + false + ); + $this->objectManagerMock->expects($this->at(1)) + ->method('create') + ->with( + \Magento\Framework\Validator\Config::class, + ['configFiles' => $this->fileIteratorMock] + ) + ->willReturn($this->validatorConfigMock); + $this->readerMock = $this->getMock( + \Magento\Framework\Module\Dir\Reader::class, + ['getConfigurationFiles'], + [], + '', + false ); + $this->cacheMock = $this->getMock(\Magento\Framework\Cache\FrontendInterface::class); - // Translate adapter mock - $this->_translateAdapter = $this->getMockBuilder( - \Magento\Framework\TranslateInterface::class - )->disableOriginalConstructor()->getMock(); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->factory = $objectManager->getObject( + \Magento\Framework\Validator\Factory::class, + [ + 'objectManager' => $this->objectManagerMock, + 'moduleReader' => $this->readerMock, + 'cache' => $this->cacheMock + ] + ); - $this->cache = $this->getMockBuilder(\Magento\Framework\Cache\FrontendInterface::class) - ->getMockForAbstractClass(); + $this->serializerMock = $this->getMock(\Magento\Framework\Serialize\SerializerInterface::class); + $this->fileIteratorFactoryMock = $this->getMock( + \Magento\Framework\Config\FileIteratorFactory::class, + [], + [], + '', + false + ); + $objectManager->setBackwardCompatibleProperty( + $this->factory, + 'serializer', + $this->serializerMock + ); + $objectManager->setBackwardCompatibleProperty( + $this->factory, + 'fileIteratorFactory', + $this->fileIteratorFactoryMock + ); } /** @@ -100,87 +141,103 @@ protected function setUp() */ protected function tearDown() { - \Magento\Framework\Validator\AbstractValidator::setDefaultTranslator($this->_defaultTranslator); - unset($this->_defaultTranslator); + \Magento\Framework\Validator\AbstractValidator::setDefaultTranslator($this->defaultTranslator); + unset($this->defaultTranslator); } - /** - * Test getValidatorConfig created correct validator config. Check that validator translator was initialized. - */ public function testGetValidatorConfig() { - $factory = new \Magento\Framework\Validator\Factory( - $this->_objectManager, - $this->_config, - $this->cache - ); - $actualConfig = $factory->getValidatorConfig(); + $this->readerMock->method('getConfigurationFiles') + ->with('validation.xml') + ->willReturn($this->fileIteratorMock); + $this->fileIteratorMock->method('toArray') + ->willReturn($this->data); + $actualConfig = $this->factory->getValidatorConfig(); $this->assertInstanceOf( \Magento\Framework\Validator\Config::class, $actualConfig, 'Object of incorrect type was created' ); - - // Check that validator translator was correctly instantiated - $validatorTranslator = \Magento\Framework\Validator\AbstractValidator::getDefaultTranslator(); $this->assertInstanceOf( \Magento\Framework\Translate\Adapter::class, - $validatorTranslator, + \Magento\Framework\Validator\AbstractValidator::getDefaultTranslator(), 'Default validator translate adapter was not set correctly' ); } - /** - * Test createValidatorBuilder call - */ + public function testGetValidatorConfigCacheNotExist() + { + $this->cacheMock->expects($this->once()) + ->method('load') + ->willReturn(false); + $this->readerMock->expects($this->once()) + ->method('getConfigurationFiles') + ->willReturn($this->fileIteratorMock); + $this->fileIteratorMock->method('toArray') + ->willReturn($this->data); + $this->cacheMock->expects($this->once()) + ->method('save') + ->with($this->jsonString); + $this->serializerMock->expects($this->once()) + ->method('serialize') + ->with($this->data) + ->willReturn($this->jsonString); + $this->factory->getValidatorConfig(); + $this->factory->getValidatorConfig(); + } + + public function testGetValidatorConfigCacheExist() + { + $this->cacheMock->expects($this->once()) + ->method('load') + ->willReturn($this->jsonString); + $this->readerMock->expects($this->never()) + ->method('getConfigurationFiles'); + $this->cacheMock->expects($this->never()) + ->method('save'); + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->with($this->jsonString) + ->willReturn($this->data); + $this->fileIteratorFactoryMock->method('create') + ->willReturn($this->fileIteratorMock); + $this->factory->getValidatorConfig(); + $this->factory->getValidatorConfig(); + } + public function testCreateValidatorBuilder() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_validatorConfig->expects( - $this->once() - )->method( - 'createValidatorBuilder' - )->with( - 'test', - 'class', - [] - )->will( - $this->returnValue( - $objectManager->getObject(\Magento\Framework\Validator\Builder::class, ['constraints' => []]) - ) - ); - $factory = new \Magento\Framework\Validator\Factory( - $this->_objectManager, - $this->_config, - $this->cache - ); + $this->readerMock->method('getConfigurationFiles') + ->with('validation.xml') + ->willReturn($this->fileIteratorMock); + $this->fileIteratorMock->method('toArray') + ->willReturn($this->data); + $builderMock = $this->getMock(\Magento\Framework\Validator\Builder::class, [], [], '', false); + $this->validatorConfigMock->expects($this->once()) + ->method('createValidatorBuilder') + ->with('test', 'class', []) + ->willReturn($builderMock); $this->assertInstanceOf( \Magento\Framework\Validator\Builder::class, - $factory->createValidatorBuilder('test', 'class', []) + $this->factory->createValidatorBuilder('test', 'class', []) ); } - /** - * Test createValidatorBuilder call - */ public function testCreateValidator() { - $this->_validatorConfig->expects( - $this->once() - )->method( - 'createValidator' - )->with( - 'test', - 'class', - [] - )->will( - $this->returnValue(new \Magento\Framework\Validator()) - ); - $factory = new \Magento\Framework\Validator\Factory( - $this->_objectManager, - $this->_config, - $this->cache + $this->readerMock->method('getConfigurationFiles') + ->with('validation.xml') + ->willReturn($this->fileIteratorMock); + $this->fileIteratorMock->method('toArray') + ->willReturn($this->data); + $validatorMock = $this->getMock(\Magento\Framework\Validator::class, [], [], '', false); + $this->validatorConfigMock->expects($this->once()) + ->method('createValidator') + ->with('test', 'class', []) + ->willReturn($validatorMock); + $this->assertInstanceOf( + \Magento\Framework\Validator::class, + $this->factory->createValidator('test', 'class', []) ); - $this->assertInstanceOf(\Magento\Framework\Validator::class, $factory->createValidator('test', 'class', [])); } } diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/LocaleTest.php b/lib/internal/Magento/Framework/Validator/Test/Unit/LocaleTest.php index 673a5616020da..d1da2ba354a61 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/LocaleTest.php +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/LocaleTest.php @@ -1,6 +1,6 @@ validator = new UrlValidator(); + } + + /** + * @param array $allowedSchemes + * @param string $url + * @param bool $expectedResult + * @dataProvider isValidDataProvider + */ + public function testIsValid(array $allowedSchemes, $url, $expectedResult) + { + $this->assertSame($expectedResult, $this->validator->isValid($url, $allowedSchemes)); + } + + /** + * @return array + */ + public function isValidDataProvider() + { + return [ + [ + 'allowedSchemes' => [], + 'url' => 'http://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => ['http'], + 'url' => 'http://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => [], + 'url' => 'https://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => ['https'], + 'url' => 'https://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => [], + 'url' => 'http://example.com_test', + 'expectedResult' => false, + ], + [ + 'allowedSchemes' => [], + 'url' => 'ftp://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => ['ftp'], + 'url' => 'ftp://example.com', + 'expectedResult' => true, + ], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/ValidatorAbstractTest.php b/lib/internal/Magento/Framework/Validator/Test/Unit/ValidatorAbstractTest.php index f7e03928fd03d..0f496c9bbae28 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/ValidatorAbstractTest.php +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/ValidatorAbstractTest.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_builder_instance.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_builder_instance.xml index 9a2c90714d526..9e4a25549084b 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_builder_instance.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_builder_instance.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_child_for_option.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_child_for_option.xml index 9aea586b4bee7..0cc14bf204049 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_child_for_option.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_child_for_option.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_constraint.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_constraint.xml index 57944f0143b2f..4b7b0a24e6772 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_constraint.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_constraint.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_content_for_callback.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_content_for_callback.xml index 3a61e4575ef85..9a07e7bc1bdf2 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_content_for_callback.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_content_for_callback.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_entity_callback.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_entity_callback.xml index ca4bd7fcd527c..8b3ff7a17ee3e 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_entity_callback.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_entity_callback.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_method.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_method.xml index c8ed7062f0b5a..a13efa48945ca 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_method.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_method.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_method_callback.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_method_callback.xml index 2b7267be73a63..14ef83699b01d 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_method_callback.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/invalid_method_callback.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/multiple_callback_in_argument.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/multiple_callback_in_argument.xml index 7d9e2a81dbd28..422d097360eef 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/multiple_callback_in_argument.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/multiple_callback_in_argument.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_class_for_constraint.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_class_for_constraint.xml index 86280ce54b062..042932dea0f17 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_class_for_constraint.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_class_for_constraint.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_constraint.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_constraint.xml index 61dcec47f583f..63b15da6656c5 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_constraint.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_constraint.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_entity.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_entity.xml index 94ee985d62511..0b848083efefc 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_entity.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_entity.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_group.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_group.xml index 17c388ee915ab..79121eb16ff65 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_group.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_group.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_rule.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_rule.xml index 483da577d5f9f..82bc468323460 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_rule.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_name_for_rule.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_rule_for_reference.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_rule_for_reference.xml index 812d6f862d839..500e52d20ed65 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_rule_for_reference.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/no_rule_for_reference.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/not_unique_use.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/not_unique_use.xml index 97468d6448e3e..07571c6164b84 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/not_unique_use.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/negative/not_unique_use.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/builder/validation.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/builder/validation.xml index fb4693b450797..9ba4ccdeb2eed 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/builder/validation.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/builder/validation.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/module_a/validation.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/module_a/validation.xml index 87348b1c340b6..97b0880fd9b8b 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/module_a/validation.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/module_a/validation.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/module_b/validation.xml b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/module_b/validation.xml index c060e26469cb1..ba3ef4c988137 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/module_b/validation.xml +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/_files/validation/positive/module_b/validation.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/Validator/Timezone.php b/lib/internal/Magento/Framework/Validator/Timezone.php index ec221fc308705..0e5f87c7724a9 100644 --- a/lib/internal/Magento/Framework/Validator/Timezone.php +++ b/lib/internal/Magento/Framework/Validator/Timezone.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/ValidatorFactory.php b/lib/internal/Magento/Framework/ValidatorFactory.php index 6508be8bf243f..efdaf300e7917 100644 --- a/lib/internal/Magento/Framework/ValidatorFactory.php +++ b/lib/internal/Magento/Framework/ValidatorFactory.php @@ -1,6 +1,6 @@ area = $areaType; $this->theme = $themePath; $this->locale = $localeCode; - $this->isSecure = $isSecure; parent::__construct($baseUrl, DirectoryList::STATIC_VIEW, $this->generatePath()); } @@ -103,6 +91,6 @@ private function generatePath() */ public function getConfigPath() { - return $this->getPath() . ($this->isSecure ? '/' . self::SECURE_PATH : ''); + return $this->getPath(); } } diff --git a/lib/internal/Magento/Framework/View/Asset/File/FallbackContextFactory.php b/lib/internal/Magento/Framework/View/Asset/File/FallbackContextFactory.php index cd4df0bc49fbf..36fcade1682bb 100644 --- a/lib/internal/Magento/Framework/View/Asset/File/FallbackContextFactory.php +++ b/lib/internal/Magento/Framework/View/Asset/File/FallbackContextFactory.php @@ -1,6 +1,6 @@ filesystem = $filesystem; } + /** + * @deprecated + * @return Source + */ + private function getAssetSource() + { + if (!$this->assetSource) { + $this->assetSource = ObjectManager::getInstance()->get(Source::class); + } + return $this->assetSource; + } + /** * {@inheritdoc} */ public function merge(array $assetsToMerge, \Magento\Framework\View\Asset\LocalInterface $resultAsset) { - $sourceDir = $this->filesystem->getDirectoryRead(DirectoryList::ROOT); + $rootDir = $this->filesystem->getDirectoryRead(DirectoryList::ROOT); $mTime = null; /** @var \Magento\Framework\View\Asset\MergeableInterface $asset */ foreach ($assetsToMerge as $asset) { - $mTime .= $sourceDir->stat($sourceDir->getRelativePath($asset->getSourceFile()))['mtime']; + $sourceFile = $this->getAssetSource()->findSource($asset); + $mTime .= $rootDir->stat($rootDir->getRelativePath($sourceFile))['mtime']; } + if (null === $mTime) { return; // nothing to merge } diff --git a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php index 68cc706ad9bdf..de9248b8fe10a 100644 --- a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php +++ b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php @@ -1,6 +1,6 @@ composeMergedContent($assetsToMerge, $resultAsset); - $dir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); - $dir->writeFile($resultAsset->getPath(), $mergedContent); + $filePath = $resultAsset->getPath(); + $staticDir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); + $tmpDir = $this->filesystem->getDirectoryWrite(DirectoryList::TMP); + $tmpDir->writeFile($filePath, $mergedContent); + $tmpDir->renameFile($filePath, $filePath, $staticDir); } /** diff --git a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/FileExists.php b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/FileExists.php index 211f3daed3c91..60339fa2d7542 100644 --- a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/FileExists.php +++ b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/FileExists.php @@ -1,6 +1,6 @@ $url, 'areaType' => $area, 'themePath' => $themePath, - 'localeCode' => $locale, - 'isSecure' => $isSecure + 'localeCode' => $locale ] ); } diff --git a/lib/internal/Magento/Framework/View/Asset/Source.php b/lib/internal/Magento/Framework/View/Asset/Source.php index cf80bd0d1b382..d59f48a136274 100644 --- a/lib/internal/Magento/Framework/View/Asset/Source.php +++ b/lib/internal/Magento/Framework/View/Asset/Source.php @@ -1,6 +1,6 @@ getCode(); + $key = $currentTheme->getFullPath(); if (isset($this->viewConfigs[$key])) { return $this->viewConfigs[$key]; } diff --git a/lib/internal/Magento/Framework/View/ConfigInterface.php b/lib/internal/Magento/Framework/View/ConfigInterface.php index 21cb20bc80815..93ec4ff6869b1 100644 --- a/lib/internal/Magento/Framework/View/ConfigInterface.php +++ b/lib/internal/Magento/Framework/View/ConfigInterface.php @@ -1,6 +1,6 @@ _loadByPath($themeKey, $area); } - if (!$themeModel->getId()) { + if (!$themeModel->getCode()) { throw new \LogicException("Unable to load theme by specified key: '{$themeKey}'"); } $this->_addTheme($themeModel); diff --git a/lib/internal/Magento/Framework/View/Design/Theme/Image.php b/lib/internal/Magento/Framework/View/Design/Theme/Image.php index c6725f07418f9..9093759526cf9 100644 --- a/lib/internal/Magento/Framework/View/Design/Theme/Image.php +++ b/lib/internal/Magento/Framework/View/Design/Theme/Image.php @@ -1,6 +1,6 @@ scopeConfig = $scopeConfig; $this->exceptionConfigPath = $exceptionConfigPath; $this->scopeType = $scopeType; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); } /** @@ -65,7 +78,7 @@ public function getThemeByRequest(\Magento\Framework\App\Request\Http $request) if (!$expressions) { return false; } - $expressions = unserialize($expressions); + $expressions = $this->serializer->unserialize($expressions); foreach ($expressions as $rule) { if (preg_match($rule['regexp'], $userAgent)) { return $rule['value']; diff --git a/lib/internal/Magento/Framework/View/DesignInterface.php b/lib/internal/Magento/Framework/View/DesignInterface.php index e2c0e387dbbb5..6429166932d8e 100644 --- a/lib/internal/Magento/Framework/View/DesignInterface.php +++ b/lib/internal/Magento/Framework/View/DesignInterface.php @@ -1,6 +1,6 @@ _eventManager->dispatch('view_block_abstract_to_html_before', ['block' => $this]); - if ($this->_scopeConfig->getValue( - 'advanced/modules_disable_output/' . $this->getModuleName(), - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - )) { - return ''; - } + $this->getModuleName(); $html = $this->_loadCache(); if ($html === false) { @@ -670,6 +665,18 @@ public function toHtml() } $html = $this->_afterToHtml($html); + /** @var \Magento\Framework\DataObject */ + $transportObject = new \Magento\Framework\DataObject( + [ + 'html' => $html, + ] + ); + $this->_eventManager->dispatch('view_block_abstract_to_html_after', [ + 'block' => $this, + 'transport' => $transportObject + ]); + $html = $transportObject->getHtml(); + return $html; } diff --git a/lib/internal/Magento/Framework/View/Element/Block/ArgumentInterface.php b/lib/internal/Magento/Framework/View/Element/Block/ArgumentInterface.php new file mode 100644 index 0000000000000..9fa51aa6e4035 --- /dev/null +++ b/lib/internal/Magento/Framework/View/Element/Block/ArgumentInterface.php @@ -0,0 +1,14 @@ +cache->load(static::CACHE_ID); if ($cachedData === false) { $data = $uiReader->read(); - $this->cache->save(serialize($data), static::CACHE_ID); + $this->cache->save($this->getSerializer()->serialize($data), static::CACHE_ID); } else { - $data = unserialize($cachedData); + $data = $this->getSerializer()->unserialize($cachedData); } $this->prepareComponentData($data); } @@ -109,4 +114,19 @@ protected function prepareComponentData(array $componentsData) $this->setComponentData($name, reset($data)); } } + + /** + * Get serializer + * + * @return \Magento\Framework\Serialize\SerializerInterface + * @deprecated + */ + private function getSerializer() + { + if ($this->serializer === null) { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\SerializerInterface::class); + } + return $this->serializer; + } } diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Config/Provider/Template.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Config/Provider/Template.php index 79d185b2b26a7..c476c650d9b16 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Config/Provider/Template.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Config/Provider/Template.php @@ -1,13 +1,10 @@ aggregatedFileCollector = $aggregatedFileCollector; @@ -81,7 +83,9 @@ public function __construct( $this->aggregatedFileCollectorFactory = $aggregatedFileCollectorFactory; $cachedTemplates = $this->cache->load(static::CACHE_ID); - $this->cachedTemplates = $cachedTemplates === false ? [] : unserialize($cachedTemplates); + $this->cachedTemplates = $cachedTemplates === false ? [] : $this->getSerializer()->unserialize( + $cachedTemplates + ); } /** @@ -104,8 +108,23 @@ public function getTemplate($template) 'domMerger' => $this->domMerger ] )->getContent(); - $this->cache->save(serialize($this->cachedTemplates), static::CACHE_ID); + $this->cache->save($this->getSerializer()->serialize($this->cachedTemplates), static::CACHE_ID); return $this->cachedTemplates[$hash]; } + + /** + * Get serializer + * + * @return \Magento\Framework\Serialize\SerializerInterface + * @deprecated + */ + private function getSerializer() + { + if ($this->serializer === null) { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\SerializerInterface::class); + } + return $this->serializer; + } } diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Config/Reader.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Config/Reader.php index e2e8efdee89e2..c8bd5afe5074a 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Config/Reader.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Config/Reader.php @@ -1,6 +1,6 @@ handles[] = $handle; + } + + /** + * Get list of handles containing entity ID + * + * @return string[] + */ + public function getHandles() + { + return $this->handles; + } +} diff --git a/lib/internal/Magento/Framework/View/File.php b/lib/internal/Magento/Framework/View/File.php index 1751ddc67de51..96c842b03f731 100644 --- a/lib/internal/Magento/Framework/View/File.php +++ b/lib/internal/Magento/Framework/View/File.php @@ -1,6 +1,6 @@ * - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\View\Helper; diff --git a/lib/internal/Magento/Framework/View/Helper/PathPattern.php b/lib/internal/Magento/Framework/View/Helper/PathPattern.php index 7ff51613dbd1a..6d677d77a3200 100644 --- a/lib/internal/Magento/Framework/View/Helper/PathPattern.php +++ b/lib/internal/Magento/Framework/View/Helper/PathPattern.php @@ -1,6 +1,6 @@ _elementClass = \Magento\Framework\View\Layout\Element::class; $this->_renderingOutput = new \Magento\Framework\DataObject(); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); $this->_processorFactory = $processorFactory; $this->_eventManager = $eventManager; @@ -308,12 +318,19 @@ public function generateElements() $cacheId = 'structure_' . $this->getUpdate()->getCacheId(); $result = $this->cache->load($cacheId); if ($result) { - $this->readerContext = unserialize($result); + $data = $this->serializer->unserialize($result); + $this->getReaderContext()->getPageConfigStructure()->populateWithArray($data['pageConfigStructure']); + $this->getReaderContext()->getScheduledStructure()->populateWithArray($data['scheduledStructure']); } else { \Magento\Framework\Profiler::start('build_structure'); $this->readerPool->interpret($this->getReaderContext(), $this->getNode()); \Magento\Framework\Profiler::stop('build_structure'); - $this->cache->save(serialize($this->getReaderContext()), $cacheId, $this->getUpdate()->getHandles()); + + $data = [ + 'pageConfigStructure' => $this->getReaderContext()->getPageConfigStructure()->__toArray(), + 'scheduledStructure' => $this->getReaderContext()->getScheduledStructure()->__toArray(), + ]; + $this->cache->save($this->serializer->serialize($data), $cacheId, $this->getUpdate()->getHandles()); } $generatorContext = $this->generatorContextFactory->create( diff --git a/lib/internal/Magento/Framework/View/Layout/Argument/Interpreter/DataObject.php b/lib/internal/Magento/Framework/View/Layout/Argument/Interpreter/DataObject.php index ae284734696ad..8b60d1acebe5f 100644 --- a/lib/internal/Magento/Framework/View/Layout/Argument/Interpreter/DataObject.php +++ b/lib/internal/Magento/Framework/View/Layout/Argument/Interpreter/DataObject.php @@ -1,6 +1,6 @@ getAttribute('remove'), FILTER_VALIDATE_BOOLEAN); if ($elementRemove) { $scheduledStructure->setElementToRemoveList($elementName); - } else { - $data = $scheduledStructure->getStructureElementData($elementName, []); - $data['attributes'] = $this->mergeBlockAttributes($data, $currentElement); - $this->updateScheduledData($currentElement, $data); - $this->evaluateArguments($currentElement, $data); - $scheduledStructure->setStructureElementData($elementName, $data); + return; + } elseif ($currentElement->getAttribute('remove')) { + $scheduledStructure->unsetElementFromListToRemove($elementName); } + $data = $scheduledStructure->getStructureElementData($elementName, []); + $data['attributes'] = $this->mergeBlockAttributes($data, $currentElement); + $this->updateScheduledData($currentElement, $data); + $this->evaluateArguments($currentElement, $data); + $scheduledStructure->setStructureElementData($elementName, $data); } /** diff --git a/lib/internal/Magento/Framework/View/Layout/Reader/Container.php b/lib/internal/Magento/Framework/View/Layout/Reader/Container.php index c4111a13c33b3..21058e26c5473 100755 --- a/lib/internal/Magento/Framework/View/Layout/Reader/Container.php +++ b/lib/internal/Magento/Framework/View/Layout/Reader/Container.php @@ -1,6 +1,6 @@ getAttribute('name'); $containerRemove = filter_var($currentElement->getAttribute('remove'), FILTER_VALIDATE_BOOLEAN); - if ($containerRemove) { $scheduledStructure->setElementToRemoveList($containerName); - } else { - $this->mergeContainerAttributes($scheduledStructure, $currentElement); + return; + } elseif ($currentElement->getAttribute('remove')) { + $scheduledStructure->unsetElementFromListToRemove($containerName); } + $this->mergeContainerAttributes($scheduledStructure, $currentElement); } } diff --git a/lib/internal/Magento/Framework/View/Layout/Reader/Context.php b/lib/internal/Magento/Framework/View/Layout/Reader/Context.php index 15ef97b031b1d..423ce6455651f 100644 --- a/lib/internal/Magento/Framework/View/Layout/Reader/Context.php +++ b/lib/internal/Magento/Framework/View/Layout/Reader/Context.php @@ -1,6 +1,6 @@ scheduledStructure = isset($data['scheduledStructure']) ? $data['scheduledStructure'] : []; - $this->scheduledData = isset($data['scheduledData']) ? $data['scheduledData'] : []; - $this->scheduledElements = isset($data['scheduledElements']) ? $data['scheduledElements'] : []; - $this->scheduledMoves = isset($data['scheduledMoves']) ? $data['scheduledMoves'] : []; - $this->scheduledRemoves = isset($data['scheduledRemoves']) ? $data['scheduledRemoves'] : []; - $this->scheduledIfconfig = isset($data['scheduledIfconfig']) ? $data['scheduledIfconfig'] : []; - $this->scheduledPaths = isset($data['scheduledPaths']) ? $data['scheduledPaths'] : []; + $this->populateWithArray($data); } /** @@ -531,4 +540,44 @@ public function flushScheduledStructure() $this->scheduledElements = []; $this->scheduledStructure = []; } + + /** + * Reformat 'Layout scheduled structure' to array. + * + * @return array + */ + public function __toArray() + { + $result = []; + foreach ($this->serializableProperties as $property) { + $result[$property] = $this->{$property}; + } + + return $result; + } + + /** + * Update 'Layout scheduled structure' data. + * + * @param array $data + * @return void + */ + public function populateWithArray(array $data) + { + foreach ($this->serializableProperties as $property) { + $this->{$property} = $this->getArrayValueByKey($property, $data); + } + } + + /** + * Get value from array by key. + * + * @param string $key + * @param array $array + * @return array + */ + private function getArrayValueByKey($key, array $array) + { + return isset($array[$key]) ? $array[$key] : []; + } } diff --git a/lib/internal/Magento/Framework/View/Layout/ScheduledStructure/Helper.php b/lib/internal/Magento/Framework/View/Layout/ScheduledStructure/Helper.php index 870f147a43634..b5b5711cb5f2c 100644 --- a/lib/internal/Magento/Framework/View/Layout/ScheduledStructure/Helper.php +++ b/lib/internal/Magento/Framework/View/Layout/ScheduledStructure/Helper.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd b/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd index 401bf2e93f366..21f27bf343a9d 100755 --- a/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd @@ -1,7 +1,7 @@ @@ -248,6 +248,7 @@ + diff --git a/lib/internal/Magento/Framework/View/Layout/etc/head.xsd b/lib/internal/Magento/Framework/View/Layout/etc/head.xsd index 7e26ed41a6941..141e42db113cb 100644 --- a/lib/internal/Magento/Framework/View/Layout/etc/head.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/head.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/Layout/etc/html.xsd b/lib/internal/Magento/Framework/View/Layout/etc/html.xsd index 24a04fe922761..ddc12f3c413d0 100644 --- a/lib/internal/Magento/Framework/View/Layout/etc/html.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/html.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/Layout/etc/layout_generic.xsd b/lib/internal/Magento/Framework/View/Layout/etc/layout_generic.xsd index 6624495d3a8ff..b174d3008e93b 100755 --- a/lib/internal/Magento/Framework/View/Layout/etc/layout_generic.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/layout_generic.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd b/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd index d6faf66fdc094..665b7504255e0 100644 --- a/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd b/lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd index b77c440ef2e6b..b4b9dcbb43948 100644 --- a/lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/Layout/etc/page_layout.xsd b/lib/internal/Magento/Framework/View/Layout/etc/page_layout.xsd index 7d62f2b903d66..9ea11a18ad23c 100755 --- a/lib/internal/Magento/Framework/View/Layout/etc/page_layout.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/page_layout.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/Layout/etc/page_types.xsd b/lib/internal/Magento/Framework/View/Layout/etc/page_types.xsd index 9cc119a646827..06088726fd598 100644 --- a/lib/internal/Magento/Framework/View/Layout/etc/page_types.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/page_types.xsd @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/LayoutFactory.php b/lib/internal/Magento/Framework/View/LayoutFactory.php index 2890ec5cb8a15..86ac802f654d2 100644 --- a/lib/internal/Magento/Framework/View/LayoutFactory.php +++ b/lib/internal/Magento/Framework/View/LayoutFactory.php @@ -1,6 +1,6 @@ getFileLayoutUpdatesXml(); foreach ($layout->xpath("*[self::handle or self::layout][@id='{$handle}']") as $updateXml) { $this->_fetchRecursiveUpdates($updateXml); - $this->addUpdate($updateXml->innerXml()); + $updateInnerXml = $updateXml->innerXml(); + $this->validateUpdate($handle, $updateInnerXml); + $this->addUpdate($updateInnerXml); } \Magento\Framework\Profiler::stop($_profilerKey); @@ -563,12 +565,31 @@ protected function _fetchDbLayoutUpdates($handle) $updateStr = $this->_substitutePlaceholders($updateStr); $updateXml = $this->_loadXmlString($updateStr); $this->_fetchRecursiveUpdates($updateXml); - $this->addUpdate($updateXml->innerXml()); + $updateInnerXml = $updateXml->innerXml(); + $this->validateUpdate($handle, $updateInnerXml); + $this->addUpdate($updateInnerXml); \Magento\Framework\Profiler::stop($_profilerKey); return (bool)$updateStr; } + /** + * Validate layout update content, throw exception on failure. + * + * This method is used as a hook for plugins. + * + * @param string $handle + * @param string $updateXml + * @return void + * @throws \Exception + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @codeCoverageIgnore + */ + public function validateUpdate($handle, $updateXml) + { + return; + } + /** * Substitute placeholders {{placeholder_name}} with their values in XML string * diff --git a/lib/internal/Magento/Framework/View/Model/Layout/Translator.php b/lib/internal/Magento/Framework/View/Model/Layout/Translator.php index 0cbc336a44fd2..07539d8a3d693 100644 --- a/lib/internal/Magento/Framework/View/Model/Layout/Translator.php +++ b/lib/internal/Magento/Framework/View/Model/Layout/Translator.php @@ -1,6 +1,6 @@ assets; } + + /** + * Reformat 'Page config structure' to array. + * + * @return array + */ + public function __toArray() + { + $result = []; + foreach ($this->serializableProperties as $property) { + $result[$property] = $this->{$property}; + } + + return $result; + } + + /** + * Update 'Page config structure' data. + * + * @param array $data + * @return void + */ + public function populateWithArray(array $data) + { + foreach ($this->serializableProperties as $property) { + $this->{$property} = $this->getArrayValueByKey($property, $data); + } + } + + /** + * Get value from array by key. + * + * @param string $key + * @param array $array + * @return array + */ + private function getArrayValueByKey($key, array $array) + { + return isset($array[$key]) ? $array[$key] : []; + } } diff --git a/lib/internal/Magento/Framework/View/Page/ConfigFactory.php b/lib/internal/Magento/Framework/View/Page/ConfigFactory.php index d0fed31c244d9..c4071567de02f 100644 --- a/lib/internal/Magento/Framework/View/Page/ConfigFactory.php +++ b/lib/internal/Magento/Framework/View/Page/ConfigFactory.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/View/Render/RenderFactory.php b/lib/internal/Magento/Framework/View/Render/RenderFactory.php index 1b96a01e84451..4fc8a7e065e4a 100644 --- a/lib/internal/Magento/Framework/View/Render/RenderFactory.php +++ b/lib/internal/Magento/Framework/View/Render/RenderFactory.php @@ -1,6 +1,6 @@ request = $context->getRequest(); $this->assetRepo = $context->getAssetRepository(); @@ -127,6 +134,8 @@ public function __construct( $this->viewFileSystem = $context->getViewFileSystem(); $this->pageConfigRendererFactory = $pageConfigRendererFactory; $this->template = $template; + $this->entitySpecificHandlesList = $entitySpecificHandlesList + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(View\EntitySpecificHandlesList::class); parent::__construct( $context, $layoutFactory, @@ -205,17 +214,23 @@ public function getConfig() * * @param array|null $parameters page parameters * @param string|null $defaultHandle + * @param bool $entitySpecific * @return bool */ - public function addPageLayoutHandles(array $parameters = [], $defaultHandle = null) + public function addPageLayoutHandles(array $parameters = [], $defaultHandle = null, $entitySpecific = true) { $handle = $defaultHandle ? $defaultHandle : $this->getDefaultLayoutHandle(); $pageHandles = [$handle]; foreach ($parameters as $key => $value) { - $pageHandles[] = $handle . '_' . $key . '_' . $value; + $pageHandle = $handle . '_' . $key . '_' . $value; + $pageHandles[] = $pageHandle; + if ($entitySpecific) { + $this->entitySpecificHandlesList->addHandle($pageHandle); + } } // Do not sort array going into add page handles. Ensure default layout handle is added first. - return $this->addHandle($pageHandles); + $this->addHandle($pageHandles); + return true; } /** diff --git a/lib/internal/Magento/Framework/View/Result/PageFactory.php b/lib/internal/Magento/Framework/View/Result/PageFactory.php index aa35b9d3e24f1..6aba62bf6a41c 100644 --- a/lib/internal/Magento/Framework/View/Result/PageFactory.php +++ b/lib/internal/Magento/Framework/View/Result/PageFactory.php @@ -1,6 +1,6 @@ fallbackContext = $this->objectManager->getObject( @@ -49,8 +47,7 @@ public function testGetConfigPath( 'baseUrl' => $baseUrl, 'areaType' => $areaType, 'themePath' => $themePath, - 'localeCode' => $localeCode, - 'isSecure' => $isSecure + 'localeCode' => $localeCode ] ); $this->assertEquals($expectedResult, $this->fallbackContext->getConfigPath()); @@ -64,7 +61,6 @@ public function getConfigPathDataProvider() 'areaType' => 'frontend', 'themePath' => 'Magento/blank', 'localeCode' => 'en_US', - 'isSecure' => false, 'expectedResult' => 'frontend/Magento/blank/en_US' ], 'https' => [ @@ -72,8 +68,7 @@ public function getConfigPathDataProvider() 'areaType' => 'frontend', 'themePath' => 'Magento/blank', 'localeCode' => 'en_US', - 'isSecure' => true, - 'expectedResult' => 'frontend/Magento/blank/en_US/secure' + 'expectedResult' => 'frontend/Magento/blank/en_US' ] ]; } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/FileTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/FileTest.php index 1ad1ab80fdef4..45c630dcf90c4 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/FileTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/FileTest.php @@ -1,6 +1,6 @@ with(DirectoryList::STATIC_VIEW) ->will($this->returnValue($this->targetDir)); $this->checksum = new Checksum($this->mergerMock, $filesystem); + $this->assetSource = $this->getMockBuilder(Source::class) + ->disableOriginalConstructor() + ->getMock(); + + $reflection = new \ReflectionClass(Checksum::class); + $reflectionProperty = $reflection->getProperty('assetSource'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->checksum, $this->assetSource); + $this->resultAsset = $this->getMock(\Magento\Framework\View\Asset\File::class, [], [], '', false); } @@ -114,9 +129,17 @@ public function testMergeMtimeUnchanged() private function getAssetsToMerge() { $one = $this->getMock(\Magento\Framework\View\Asset\File::class, [], [], '', false); - $one->expects($this->once())->method('getSourceFile')->will($this->returnValue('/dir/file/one.txt')); $two = $this->getMock(\Magento\Framework\View\Asset\File::class, [], [], '', false); - $two->expects($this->once())->method('getSourceFile')->will($this->returnValue('/dir/file/two.txt')); + $one->expects($this->never()) + ->method('getSourceFile'); + $two->expects($this->never()) + ->method('getSourceFile'); + + $this->assetSource->expects($this->exactly(2)) + ->method('findSource') + ->withConsecutive([$one], [$two]) + ->willReturnOnConsecutiveCalls('/dir/file/one.txt', '/dir/file/two.txt'); + $this->sourceDir->expects($this->exactly(2)) ->method('getRelativePath') ->will($this->onConsecutiveCalls('file/one.txt', 'file/two.txt')); diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php index 23041feb2b8c6..33ad9608109e9 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php @@ -1,10 +1,11 @@ cssUrlResolver = $this->getMock(\Magento\Framework\View\Url\CssResolver::class); $filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false); - $this->writeDir = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\WriteInterface::class); + $this->staticDir = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(); + $this->tmpDir = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(); $filesystem->expects($this->any()) ->method('getDirectoryWrite') - ->with(DirectoryList::STATIC_VIEW) - ->will($this->returnValue($this->writeDir)); + ->willReturnMap([ + [DirectoryList::STATIC_VIEW, \Magento\Framework\Filesystem\DriverPool::FILE, $this->staticDir], + [DirectoryList::TMP, \Magento\Framework\Filesystem\DriverPool::FILE, $this->tmpDir], + ]); $this->resultAsset = $this->getMock(\Magento\Framework\View\Asset\File::class, [], [], '', false); $this->object = new Direct($filesystem, $this->cssUrlResolver); } @@ -47,7 +56,9 @@ protected function setUp() public function testMergeNoAssets() { $this->resultAsset->expects($this->once())->method('getPath')->will($this->returnValue('foo/result')); - $this->writeDir->expects($this->once())->method('writeFile')->with('foo/result', ''); + $this->staticDir->expects($this->never())->method('writeFile'); + $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', ''); + $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir); $this->object->merge([], $this->resultAsset); } @@ -55,7 +66,9 @@ public function testMergeGeneric() { $this->resultAsset->expects($this->once())->method('getPath')->will($this->returnValue('foo/result')); $assets = $this->prepareAssetsToMerge([' one', 'two']); // note leading space intentionally - $this->writeDir->expects($this->once())->method('writeFile')->with('foo/result', 'onetwo'); + $this->staticDir->expects($this->never())->method('writeFile'); + $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', 'onetwo'); + $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir); $this->object->merge($assets, $this->resultAsset); } @@ -73,7 +86,9 @@ public function testMergeCss() ->method('aggregateImportDirectives') ->with('12') ->will($this->returnValue('1020')); - $this->writeDir->expects($this->once())->method('writeFile')->with('foo/result', '1020'); + $this->staticDir->expects($this->never())->method('writeFile'); + $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', '1020'); + $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir); $this->object->merge($assets, $this->resultAsset); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/FileExistsTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/FileExistsTest.php index 2354411700ce8..d42ad9c988429 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/FileExistsTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/FileExistsTest.php @@ -1,6 +1,6 @@ '', 'areaType' => '', 'themePath' => 'Default', - 'localeCode' => '', - 'isSecure' => '', + 'localeCode' => '' ] ) ->willReturn($fallbackContextMock); @@ -251,8 +250,7 @@ public function testGetStaticViewFileContext() 'baseUrl' => '', 'areaType' => 'area', 'themePath' => '', - 'localeCode' => 'locale', - 'isSecure' => '', + 'localeCode' => 'locale' ] ) ->willReturn($fallbackContextMock); diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php index 2a704650aa9dd..fdc14e333e839 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php @@ -1,6 +1,6 @@ getMock( \Magento\Theme\Model\Theme::class, - ['getCode'], + ['getFullPath'], [], '', false ); $themeMock->expects($this->atLeastOnce()) - ->method('getCode') + ->method('getFullPath') ->will($this->returnValue($themeCode)); $params = [ 'themeModel' => $themeMock, diff --git a/lib/internal/Magento/Framework/View/Test/Unit/ContextTest.php b/lib/internal/Magento/Framework/View/Test/Unit/ContextTest.php index 59e5b0e7a43f8..7225fee0c0ef4 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/ContextTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/ContextTest.php @@ -1,6 +1,6 @@ getMock(\Magento\Theme\Model\Theme::class, [], [], '', false); - $theme->expects($this->exactly(3))->method('getId')->will($this->returnValue($expectedId)); + $theme->expects($this->exactly(2))->method('getId')->will($this->returnValue($expectedId)); $theme->expects($this->once())->method('getFullPath')->will($this->returnValue(null)); - + $theme->expects($this->once())->method('getCode')->willReturn($expectedId); $this->themeProviderMock->expects( $this->once() )->method( @@ -70,10 +70,10 @@ public function testCreateByPath() $path = 'frontend/Magento/luma'; $themeId = 7; $theme = $this->getMock(\Magento\Theme\Model\Theme::class, [], [], '', false); - $theme->expects($this->exactly(3))->method('getId')->will($this->returnValue($themeId)); + $theme->expects($this->exactly(2))->method('getId')->will($this->returnValue($themeId)); $theme->expects($this->once())->method('getFullPath')->will($this->returnValue($path)); - + $theme->expects($this->once())->method('getCode')->willReturn('Magento/luma'); $this->themeProviderMock->expects( $this->once() )->method( @@ -95,7 +95,6 @@ public function testCreateDummy() { $themeId = 0; $theme = $this->getMock(\Magento\Theme\Model\Theme::class, [], [], '', false); - $theme->expects($this->once())->method('getId')->will($this->returnValue($themeId)); $this->themeProviderMock->expects( $this->once() diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Design/Theme/Image/UploaderTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Design/Theme/Image/UploaderTest.php index 7a33830bc8cfb..66ceedc030d03 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Design/Theme/Image/UploaderTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Design/Theme/Image/UploaderTest.php @@ -1,6 +1,6 @@ scopeConfigMock = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); $this->requestMock = $this->getMock(\Magento\Framework\App\Request\Http::class, [], [], '', false); + $this->serializerMock = $this->getMock(Json::class, [], [], '', false); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->designExceptions = $this->objectManagerHelper->getObject( @@ -39,32 +44,39 @@ protected function setUp() [ 'scopeConfig' => $this->scopeConfigMock, 'exceptionConfigPath' => $this->exceptionConfigPath, - 'scopeType' => $this->scopeType + 'scopeType' => $this->scopeType, + 'serializer' => $this->serializerMock, ] ); } /** * @param string $userAgent - * @param bool $useConfig + * @param bool $configValue + * @param int $callNum * @param bool|string $result * @param array $expressions * @dataProvider getThemeByRequestDataProvider */ - public function testGetThemeByRequest($userAgent, $useConfig, $result, $expressions = []) + public function testGetThemeByRequest($userAgent, $configValue, $callNum, $result, $expressions = []) { $this->requestMock->expects($this->once()) ->method('getServer') ->with($this->equalTo('HTTP_USER_AGENT')) ->will($this->returnValue($userAgent)); - if ($useConfig) { + if ($userAgent) { $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with($this->equalTo($this->exceptionConfigPath), $this->equalTo($this->scopeType)) - ->will($this->returnValue(serialize($expressions))); + ->will($this->returnValue($configValue)); } + $this->serializerMock->expects($this->exactly($callNum)) + ->method('unserialize') + ->with($configValue) + ->willReturn($expressions); + $this->assertSame($result, $this->designExceptions->getThemeByRequest($this->requestMock)); } @@ -74,11 +86,11 @@ public function testGetThemeByRequest($userAgent, $useConfig, $result, $expressi public function getThemeByRequestDataProvider() { return [ - [false, false, false], - ['iphone', false, false], - ['iphone', true, false], - ['iphone', true, 'matched', [['regexp' => '/iphone/', 'value' => 'matched']]], - ['explorer', true, false, [['regexp' => '/iphone/', 'value' => 'matched']]], + [false, null, 0, false], + ['iphone', null, 0, false], + ['iphone', 'serializedExpressions1', 1, false], + ['iphone', 'serializedExpressions2', 1, 'matched', [['regexp' => '/iphone/', 'value' => 'matched']]], + ['explorer', 'serializedExpressions3', 1, false, [['regexp' => '/iphone/', 'value' => 'matched']]], ]; } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/DesignLoaderTest.php b/lib/internal/Magento/Framework/View/Test/Unit/DesignLoaderTest.php index 58331e32deda4..9435c1a09e311 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/DesignLoaderTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/DesignLoaderTest.php @@ -1,6 +1,6 @@ block->setData('module_name', $moduleName); - $this->eventManagerMock->expects($this->once()) + $this->eventManagerMock->expects($this->exactly(2)) ->method('dispatch') - ->with('view_block_abstract_to_html_before', ['block' => $this->block]); - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('advanced/modules_disable_output/' . $moduleName, \Magento\Store\Model\ScopeInterface::SCOPE_STORE) - ->willReturn(true); + ->willReturnMap([ + ['view_block_abstract_to_html_before', ['block' => $this->block]], + ['view_block_abstract_to_html_after', ['block' => $this->block]], + ]); $this->assertSame('', $this->block->toHtml()); } @@ -219,6 +218,7 @@ public function testToHtmlWhenModuleIsDisabled() * @param string|bool $cacheLifetime * @param string|bool $dataFromCache * @param string $dataForSaveCache + * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $expectsDispatchEvent * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $expectsCacheLoad * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $expectsCacheSave * @param string $expectedResult @@ -229,6 +229,7 @@ public function testGetCacheLifetimeViaToHtml( $cacheLifetime, $dataFromCache, $dataForSaveCache, + $expectsDispatchEvent, $expectsCacheLoad, $expectsCacheSave, $expectedResult @@ -239,13 +240,8 @@ public function testGetCacheLifetimeViaToHtml( $this->block->setData('module_name', $moduleName); $this->block->setData('cache_lifetime', $cacheLifetime); - $this->eventManagerMock->expects($this->once()) - ->method('dispatch') - ->with('view_block_abstract_to_html_before', ['block' => $this->block]); - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('advanced/modules_disable_output/' . $moduleName, \Magento\Store\Model\ScopeInterface::SCOPE_STORE) - ->willReturn(false); + $this->eventManagerMock->expects($expectsDispatchEvent) + ->method('dispatch'); $this->cacheStateMock->expects($this->any()) ->method('isEnabled') ->with(AbstractBlock::CACHE_GROUP) @@ -278,6 +274,7 @@ public function getCacheLifetimeDataProvider() 'cacheLifetime' => null, 'dataFromCache' => 'dataFromCache', 'dataForSaveCache' => '', + 'expectsDispatchEvent' => $this->exactly(2), 'expectsCacheLoad' => $this->never(), 'expectsCacheSave' => $this->never(), 'expectedResult' => '', @@ -286,6 +283,7 @@ public function getCacheLifetimeDataProvider() 'cacheLifetime' => false, 'dataFromCache' => 'dataFromCache', 'dataForSaveCache' => '', + 'expectsDispatchEvent' => $this->exactly(2), 'expectsCacheLoad' => $this->once(), 'expectsCacheSave' => $this->never(), 'expectedResult' => 'dataFromCache', @@ -294,6 +292,7 @@ public function getCacheLifetimeDataProvider() 'cacheLifetime' => 120, 'dataFromCache' => 'dataFromCache', 'dataForSaveCache' => '', + 'expectsDispatchEvent' => $this->exactly(2), 'expectsCacheLoad' => $this->once(), 'expectsCacheSave' => $this->never(), 'expectedResult' => 'dataFromCache', @@ -302,6 +301,7 @@ public function getCacheLifetimeDataProvider() 'cacheLifetime' => '120string', 'dataFromCache' => 'dataFromCache', 'dataForSaveCache' => '', + 'expectsDispatchEvent' => $this->exactly(2), 'expectsCacheLoad' => $this->once(), 'expectsCacheSave' => $this->never(), 'expectedResult' => 'dataFromCache', @@ -310,6 +310,7 @@ public function getCacheLifetimeDataProvider() 'cacheLifetime' => 120, 'dataFromCache' => false, 'dataForSaveCache' => '', + 'expectsDispatchEvent' => $this->exactly(2), 'expectsCacheLoad' => $this->once(), 'expectsCacheSave' => $this->once(), 'expectedResult' => '', diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/BlockFactoryTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/BlockFactoryTest.php index c430e8a31f756..19312ced6fad5 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/BlockFactoryTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/BlockFactoryTest.php @@ -1,6 +1,6 @@ entitySpecificHandlesList = $objectManager->getObject(EntitySpecificHandlesList::class); + } + + public function testAddAndGetHandles() + { + $this->assertEquals([], $this->entitySpecificHandlesList->getHandles()); + $this->entitySpecificHandlesList->addHandle('handle1'); + $this->entitySpecificHandlesList->addHandle('handle2'); + $this->assertEquals(['handle1', 'handle2'], $this->entitySpecificHandlesList->getHandles()); + } +} diff --git a/lib/internal/Magento/Framework/View/Test/Unit/File/Collector/BaseTest.php b/lib/internal/Magento/Framework/View/Test/Unit/File/Collector/BaseTest.php index 25b3bededf04d..ff6f3c94cadec 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/File/Collector/BaseTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/File/Collector/BaseTest.php @@ -1,6 +1,6 @@ self::EXPECTED_CLASS]; - $this->_objectManager->expects( - $this->once() - )->method( - 'create' - )->with( - self::EXPECTED_CLASS - )->will( - $this->returnValue($this) - ); + $input = ['name' => 'dataSource', 'value' => self::EXPECTED_CLASS]; + $this->_objectManager->expects($this->once()) + ->method('create') + ->with(self::EXPECTED_CLASS) + ->willReturn($this); $actual = $this->_model->evaluate($input); $this->assertSame($this, $actual); @@ -56,17 +51,18 @@ public function testEvaluateWrongClass($input, $expectedException, $expectedExce { $this->setExpectedException($expectedException, $expectedExceptionMessage); $self = $this; - $this->_objectManager->expects($this->any())->method('create')->will( - $this->returnCallback( - function ($className) use ($self) { - return $self->getMock($className); - } - ) + $this->_objectManager->expects($this->any())->method('create')->willReturnCallback( + function ($className) use ($self) { + return $self->getMock($className); + } ); $this->_model->evaluate($input); } + /** + * @return array + */ public function evaluateWrongClassDataProvider() { return [ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/OptionsTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/OptionsTest.php index 87bdef315a5c8..b2a30b5cb1290 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/OptionsTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Argument/Interpreter/OptionsTest.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/BuilderFactoryTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/BuilderFactoryTest.php index 6afd0ca712149..bbbc2f252b480 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/BuilderFactoryTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/BuilderFactoryTest.php @@ -1,6 +1,6 @@ scheduledStructure->expects($this->once()) + ->method('unsetElementFromListToRemove') + ->with($literal); + } + $this->context->expects($this->once())->method('getScheduledStructure') ->will($this->returnValue($this->scheduledStructure)); diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Reader/ContainerTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Reader/ContainerTest.php index 4e10a5a6a0841..430ec56230f0b 100755 --- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Reader/ContainerTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Reader/ContainerTest.php @@ -1,6 +1,6 @@ with($contextMock, $elementCurrent) ->willReturnSelf(); + if ($elementCurrent->getAttribute('remove') == 'false') { + $scheduledStructureMock->expects($this->once()) + ->method('unsetElementFromListToRemove') + ->with($elementCurrent->getAttribute('name')); + } + $this->container->interpret($contextMock, $elementCurrent); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Reader/FactoryTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Reader/FactoryTest.php index a91ac3b38709d..a6608ad6c053c 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/Reader/FactoryTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/Reader/FactoryTest.php @@ -1,6 +1,6 @@ expects($this->once())->method('setStructureElementData')->with( $element->getAttribute('name'), - ['attributes' => ['group' => '', 'component' => 'listing']] + ['attributes' => ['group' => '', 'component' => 'listing', 'acl' => 'test', 'condition' => 'test']] ); $scheduleStructure->expects($this->once())->method('setElementToIfconfigList')->with( $element->getAttribute('name'), @@ -88,7 +88,12 @@ public function interpretDataProvider() return [ [ $this->getElement( - '', + '', 'uiComponent' ), ] diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/ReaderPoolTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/ReaderPoolTest.php index 9dddead0e2af8..440218fb5376f 100755 --- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/ReaderPoolTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/ReaderPoolTest.php @@ -1,6 +1,6 @@ test test - \ No newline at end of file + diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/_files/arguments.xml b/lib/internal/Magento/Framework/View/Test/Unit/Layout/_files/arguments.xml index 3be95e59dfa11..04b102f7aa0df 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/_files/arguments.xml +++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/_files/arguments.xml @@ -1,7 +1,7 @@ @@ -33,7 +33,7 @@ Excel XML - Magento\Sales\Model\Order\Grid\Massaction\ItemsUpdater + Magento\Framework\View\Layout\Argument\UpdaterInterface Move to Archive */sales_archive/massAdd diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Layout/_files/invalidLayoutArgumentsXmlArray.php b/lib/internal/Magento/Framework/View/Test/Unit/Layout/_files/invalidLayoutArgumentsXmlArray.php index c6ec5526e4033..810692b94fa49 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Layout/_files/invalidLayoutArgumentsXmlArray.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Layout/_files/invalidLayoutArgumentsXmlArray.php @@ -1,6 +1,6 @@ structureMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Data\Structure::class) @@ -141,9 +158,22 @@ protected function setUp() $this->readerContextFactoryMock = $this->getMockBuilder( \Magento\Framework\View\Layout\Reader\ContextFactory::class )->disableOriginalConstructor()->getMock(); + + $this->pageConfigStructure = $this->getMockBuilder(\Magento\Framework\View\Page\Config\Structure::class) + ->setMethods(['__toArray', 'populateWithArray']) + ->getMock(); + $this->layoutScheduledSructure = $this->getMockBuilder(\Magento\Framework\View\Layout\ScheduledStructure::class) + ->setMethods(['__toArray', 'populateWithArray']) + ->getMock(); $this->readerContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Reader\Context::class) + ->setMethods(['getPageConfigStructure', 'getScheduledStructure']) ->disableOriginalConstructor() ->getMock(); + $this->readerContextMock->expects($this->any())->method('getPageConfigStructure') + ->willReturn($this->pageConfigStructure); + $this->readerContextMock->expects($this->any())->method('getScheduledStructure') + ->willReturn($this->layoutScheduledSructure); + $this->generatorContextFactoryMock = $this->getMockBuilder( \Magento\Framework\View\Layout\Generator\ContextFactory::class ) @@ -154,6 +184,15 @@ protected function setUp() ->getMock(); $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) ->getMock(); + $this->serializer = $this->getMock(\Magento\Framework\Serialize\SerializerInterface::class); + $this->serializer->expects($this->any())->method('serialize') + ->willReturnCallback(function ($value) { + return json_encode($value); + }); + $this->serializer->expects($this->any())->method('unserialize') + ->willReturnCallback(function ($value) { + return json_decode($value, true); + }); $this->model = new \Magento\Framework\View\Layout( $this->processorFactoryMock, @@ -168,7 +207,8 @@ protected function setUp() $this->generatorContextFactoryMock, $this->appStateMock, $this->loggerMock, - true + true, + $this->serializer ); } @@ -735,9 +775,32 @@ public function testGenerateElementsWithoutCache() ->with($this->readerContextMock, $xml) ->willReturnSelf(); + $pageConfigStructureData = [ + 'field_1' => 123, + 'field_2' => 'text', + 'field_3' => [ + 'field_3_1' => '1244', + 'field_3_2' => null, + 'field_3_3' => false, + ] + ]; + $this->pageConfigStructure->expects($this->any())->method('__toArray') + ->willReturn($pageConfigStructureData); + + $layoutScheduledStructureData = [ + 'field_1' => 1283, + 'field_2' => 'text_qwertyuiop[]asdfghjkl;' + ]; + $this->layoutScheduledSructure->expects($this->any())->method('__toArray') + ->willReturn($layoutScheduledStructureData); + $data = [ + 'pageConfigStructure' => $pageConfigStructureData, + 'scheduledStructure' => $layoutScheduledStructureData + ]; + $this->cacheMock->expects($this->once()) ->method('save') - ->with(serialize($this->readerContextMock), 'structure_' . $layoutCacheId, $handles) + ->with(json_encode($data), 'structure_' . $layoutCacheId, $handles) ->willReturn(true); $generatorContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Generator\Context::class) @@ -774,6 +837,9 @@ public function testGenerateElementsWithCache() $xml = simplexml_load_string('', \Magento\Framework\View\Layout\Element::class); $this->model->setXml($xml); + $this->readerContextFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->readerContextMock); $themeMock = $this->getMockForAbstractClass(\Magento\Framework\View\Design\ThemeInterface::class); $this->themeResolverMock->expects($this->once()) ->method('get') @@ -787,14 +853,33 @@ public function testGenerateElementsWithCache() ->method('getCacheId') ->willReturn($layoutCacheId); - $readerContextMock = $this->getMockBuilder(\Magento\Framework\View\Layout\Reader\Context::class) - ->disableOriginalConstructor() - ->getMock(); + $pageConfigStructureData = [ + 'field_1' => 123, + 'field_2' => 'text', + 'field_3' => [ + 'field_3_1' => '1244', + 'field_3_2' => null, + 'field_3_3' => false, + ] + ]; + $this->pageConfigStructure->expects($this->once())->method('populateWithArray') + ->with($pageConfigStructureData); + + $layoutScheduledStructureData = [ + 'field_1' => 1283, + 'field_2' => 'text_qwertyuiop[]asdfghjkl;' + ]; + $this->layoutScheduledSructure->expects($this->once())->method('populateWithArray') + ->with($layoutScheduledStructureData); + $data = [ + 'pageConfigStructure' => $pageConfigStructureData, + 'scheduledStructure' => $layoutScheduledStructureData + ]; $this->cacheMock->expects($this->once()) ->method('load') ->with('structure_' . $layoutCacheId) - ->willReturn(serialize($readerContextMock)); + ->willReturn(json_encode($data)); $this->readerPoolMock->expects($this->never()) ->method('interpret'); @@ -811,7 +896,7 @@ public function testGenerateElementsWithCache() $this->generatorPoolMock->expects($this->once()) ->method('process') - ->with($readerContextMock, $generatorContextMock) + ->with($this->readerContextMock, $generatorContextMock) ->willReturn(true); $elements = [ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Model/Layout/MergeTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Model/Layout/MergeTest.php index 561c48b915de3..48fdff2fe0960 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Model/Layout/MergeTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Model/Layout/MergeTest.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/_files/template_head.xml b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/_files/template_head.xml index 9ed7ae97f18e7..27c6a73879a0f 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/_files/template_head.xml +++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/_files/template_head.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/_files/template_html.xml b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/_files/template_html.xml index b6e011039ff89..35c4306ee74a5 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/_files/template_html.xml +++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/_files/template_html.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php index 32f319f51c732..9ae9241e739f8 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php @@ -1,6 +1,6 @@ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/PageLayout/_files/layouts_two.xml b/lib/internal/Magento/Framework/View/Test/Unit/PageLayout/_files/layouts_two.xml index 285dd37a4c2b4..6103558052070 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/PageLayout/_files/layouts_two.xml +++ b/lib/internal/Magento/Framework/View/Test/Unit/PageLayout/_files/layouts_two.xml @@ -1,7 +1,7 @@ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Render/RenderFactoryTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Render/RenderFactoryTest.php index 0ae4cf420a4ec..ea88eb9f10889 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Render/RenderFactoryTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Render/RenderFactoryTest.php @@ -1,6 +1,6 @@ with(['pageConfig' => $this->pageConfig]) ->willReturn($this->pageConfigRenderer); + $this->entitySpecificHandlesListMock = $this->getMock(EntitySpecificHandlesList::class, [], [], '', false); + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->page = $objectManagerHelper->getObject( \Magento\Framework\View\Result\Page::class, @@ -128,6 +136,7 @@ protected function setUp() 'context' => $this->context, 'translateInline' => $this->translateInline, 'pageConfigRendererFactory' => $pageConfigRendererFactory, + 'entitySpecificHandlesList' => $this->entitySpecificHandlesListMock ] ); } @@ -221,9 +230,41 @@ public function testAddPageLayoutHandles() ->with($expected) ->willReturnSelf(); + $this->entitySpecificHandlesListMock->expects($this->at(0)) + ->method('addHandle')->with('full_action_name_key_one_val_one'); + $this->entitySpecificHandlesListMock->expects($this->at(1)) + ->method('addHandle')->with('full_action_name_key_two_val_two'); + $this->page->addPageLayoutHandles($parameters, $defaultHandle); } + public function testAddPageLayoutHandlesNotEntitySpecific() + { + $fullActionName = 'Full_Action_Name'; + $defaultHandle = null; + $parameters = [ + 'key_one' => 'val_one', + 'key_two' => 'val_two', + ]; + $expected = [ + 'full_action_name', + 'full_action_name_key_one_val_one', + 'full_action_name_key_two_val_two', + ]; + $this->request->expects($this->any()) + ->method('getFullActionName') + ->will($this->returnValue($fullActionName)); + + $this->layoutMerge->expects($this->any()) + ->method('addHandle') + ->with($expected) + ->willReturnSelf(); + + $this->entitySpecificHandlesListMock->expects($this->never())->method('addHandle'); + + $this->page->addPageLayoutHandles($parameters, $defaultHandle, false); + } + public function testAddPageLayoutHandlesWithDefaultHandle() { $defaultHandle = 'default_handle'; diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Template/Html/MinifierTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Template/Html/MinifierTest.php index fb921f3776b9b..0ab955a06f896 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Template/Html/MinifierTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Template/Html/MinifierTest.php @@ -1,6 +1,6 @@ @@ -160,7 +160,7 @@ public function testMinify() TEXT; $expectedContent = << Test titleText Link some textsomeMethod(); ?>
    +
    + Snippet: +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    DirectiveHowSourceRendered
    ng-bind-htmlAutomatically uses $sanitize
    <div ng-bind-html="snippet">
    </div>
    ng-bind-htmlBypass $sanitize by explicitly trusting the dangerous value +
    <div ng-bind-html="deliberatelyTrustDangerousSnippet()">
    +     </div>
    +
    ng-bindAutomatically escapes
    <div ng-bind="snippet">
    </div>
    +
    + + + it('should sanitize the html snippet by default', function() { + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('

    an html\nclick here\nsnippet

    '); + }); + it('should inline raw snippet if bound to a trusted value', function() { + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()). + toBe("

    an html\n" + + "click here\n" + + "snippet

    "); + }); + it('should escape snippet without any filter', function() { + expect(element(by.css('#bind-default div')).getInnerHtml()). + toBe("<p style=\"color:blue\">an html\n" + + "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + + "snippet</p>"); + }); + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new text'); + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('new text'); + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe( + 'new text'); + expect(element(by.css('#bind-default div')).getInnerHtml()).toBe( + "new <b onclick=\"alert(1)\">text</b>"); + }); +
    + + */ + function $SanitizeProvider() { + this.$get = ['$$sanitizeUri', function($$sanitizeUri) { + return function(html) { + var buf = []; + htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) { + return !/^unsafe/.test($$sanitizeUri(uri, isImage)); + })); + return buf.join(''); + }; + }]; + } + + function sanitizeText(chars) { + var buf = []; + var writer = htmlSanitizeWriter(buf, angular.noop); + writer.chars(chars); + return buf.join(''); + } + + +// Regular Expressions for parsing tags and attributes + var START_TAG_REGEXP = + /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/, + END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/, + ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, + BEGIN_TAG_REGEXP = /^/g, + DOCTYPE_REGEXP = /]*?)>/i, + CDATA_REGEXP = //g, + // Match everything outside of normal chars and " (quote character) + NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; + + +// Good source of info about elements and attributes +// http://dev.w3.org/html5/spec/Overview.html#semantics +// http://simon.html5.org/html-elements + +// Safe Void Elements - HTML5 +// http://dev.w3.org/html5/spec/Overview.html#void-elements + var voidElements = makeMap("area,br,col,hr,img,wbr"); + +// Elements that you can, intentionally, leave open (and which close themselves) +// http://dev.w3.org/html5/spec/Overview.html#optional-tags + var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), + optionalEndTagInlineElements = makeMap("rp,rt"), + optionalEndTagElements = angular.extend({}, + optionalEndTagInlineElements, + optionalEndTagBlockElements); + +// Safe Block Elements - HTML5 + var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," + + "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," + + "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")); + +// Inline Elements - HTML5 + var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," + + "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," + + "samp,small,span,strike,strong,sub,sup,time,tt,u,var")); + + +// Special Elements (can contain anything) + var specialElements = makeMap("script,style"); + + var validElements = angular.extend({}, + voidElements, + blockElements, + inlineElements, + optionalEndTagElements); + +//Attributes that have href and hence need to be sanitized + var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap"); + var validAttrs = angular.extend({}, uriAttrs, makeMap( + 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ + 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ + 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ + 'scope,scrolling,shape,size,span,start,summary,target,title,type,'+ + 'valign,value,vspace,width')); + + function makeMap(str) { + var obj = {}, items = str.split(','), i; + for (i = 0; i < items.length; i++) obj[items[i]] = true; + return obj; + } + + + /** + * @example + * htmlParser(htmlString, { + * start: function(tag, attrs, unary) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * }); + * + * @param {string} html string + * @param {object} handler + */ + function htmlParser( html, handler ) { + var index, chars, match, stack = [], last = html; + stack.last = function() { return stack[ stack.length - 1 ]; }; + + while ( html ) { + chars = true; + + // Make sure we're not in a script or style element + if ( !stack.last() || !specialElements[ stack.last() ] ) { + + // Comment + if ( html.indexOf("", index) === index) { + if (handler.comment) handler.comment( html.substring( 4, index ) ); + html = html.substring( index + 3 ); + chars = false; + } + // DOCTYPE + } else if ( DOCTYPE_REGEXP.test(html) ) { + match = html.match( DOCTYPE_REGEXP ); + + if ( match ) { + html = html.replace( match[0] , ''); + chars = false; + } + // end tag + } else if ( BEGING_END_TAGE_REGEXP.test(html) ) { + match = html.match( END_TAG_REGEXP ); + + if ( match ) { + html = html.substring( match[0].length ); + match[0].replace( END_TAG_REGEXP, parseEndTag ); + chars = false; + } + + // start tag + } else if ( BEGIN_TAG_REGEXP.test(html) ) { + match = html.match( START_TAG_REGEXP ); + + if ( match ) { + html = html.substring( match[0].length ); + match[0].replace( START_TAG_REGEXP, parseStartTag ); + chars = false; + } + } + + if ( chars ) { + index = html.indexOf("<"); + + var text = index < 0 ? html : html.substring( 0, index ); + html = index < 0 ? "" : html.substring( index ); + + if (handler.chars) handler.chars( decodeEntities(text) ); + } + + } else { + html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), + function(all, text){ + text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1"); + + if (handler.chars) handler.chars( decodeEntities(text) ); + + return ""; + }); + + parseEndTag( "", stack.last() ); + } + + if ( html == last ) { + throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " + + "of html: {0}", html); + } + last = html; + } + + // Clean up any remaining tags + parseEndTag(); + + function parseStartTag( tag, tagName, rest, unary ) { + tagName = angular.lowercase(tagName); + if ( blockElements[ tagName ] ) { + while ( stack.last() && inlineElements[ stack.last() ] ) { + parseEndTag( "", stack.last() ); + } + } + + if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) { + parseEndTag( "", tagName ); + } + + unary = voidElements[ tagName ] || !!unary; + + if ( !unary ) + stack.push( tagName ); + + var attrs = {}; + + rest.replace(ATTR_REGEXP, + function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) { + var value = doubleQuotedValue + || singleQuotedValue + || unquotedValue + || ''; + + attrs[name] = decodeEntities(value); + }); + if (handler.start) handler.start( tagName, attrs, unary ); + } + + function parseEndTag( tag, tagName ) { + var pos = 0, i; + tagName = angular.lowercase(tagName); + if ( tagName ) + // Find the closest opened tag of the same type + for ( pos = stack.length - 1; pos >= 0; pos-- ) + if ( stack[ pos ] == tagName ) + break; + + if ( pos >= 0 ) { + // Close all the open elements, up the stack + for ( i = stack.length - 1; i >= pos; i-- ) + if (handler.end) handler.end( stack[ i ] ); + + // Remove the open elements from the stack + stack.length = pos; + } + } + } + + var hiddenPre=document.createElement("pre"); + var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/; + /** + * decodes all entities into regular string + * @param value + * @returns {string} A string with decoded entities. + */ + function decodeEntities(value) { + if (!value) { return ''; } + + // Note: IE8 does not preserve spaces at the start/end of innerHTML + // so we must capture them and reattach them afterward + var parts = spaceRe.exec(value); + var spaceBefore = parts[1]; + var spaceAfter = parts[3]; + var content = parts[2]; + if (content) { + hiddenPre.innerHTML=content.replace(//g, '>'); + } + + /** + * create an HTML/XML writer which writes to buffer + * @param {Array} buf use buf.jain('') to get out sanitized html string + * @returns {object} in the form of { + * start: function(tag, attrs, unary) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * } + */ + function htmlSanitizeWriter(buf, uriValidator){ + var ignore = false; + var out = angular.bind(buf, buf.push); + return { + start: function(tag, attrs, unary){ + tag = angular.lowercase(tag); + if (!ignore && specialElements[tag]) { + ignore = tag; + } + if (!ignore && validElements[tag] === true) { + out('<'); + out(tag); + angular.forEach(attrs, function(value, key){ + var lkey=angular.lowercase(key); + var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); + if (validAttrs[lkey] === true && + (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { + out(' '); + out(key); + out('="'); + out(encodeEntities(value)); + out('"'); + } + }); + out(unary ? '/>' : '>'); + } + }, + end: function(tag){ + tag = angular.lowercase(tag); + if (!ignore && validElements[tag] === true) { + out(''); + } + if (tag == ignore) { + ignore = false; + } + }, + chars: function(chars){ + if (!ignore) { + out(encodeEntities(chars)); + } + } + }; + } + + +// define ngSanitize module and register $sanitize service + angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider); + + /* global sanitizeText: false */ + + /** + * @ngdoc filter + * @name linky + * @function + * + * @description + * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and + * plain email address links. + * + * Requires the {@link ngSanitize `ngSanitize`} module to be installed. + * + * @param {string} text Input text. + * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in. + * @returns {string} Html-linkified text. + * + * @usage + + * + * @example + + + +
    + Snippet: + + + + + + + + + + + + + + + + + + + + + +
    FilterSourceRendered
    linky filter +
    <div ng-bind-html="snippet | linky">
    </div>
    +
    +
    +
    linky target +
    <div ng-bind-html="snippetWithTarget | linky:'_blank'">
    </div>
    +
    +
    +
    no filter
    <div ng-bind="snippet">
    </div>
    + + + it('should linkify the snippet with urls', function() { + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(4); + }); + it('should not linkify snippet without the linky filter', function() { + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#escaped-html a')).count()).toEqual(0); + }); + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new http://link.'); + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('new http://link.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(1); + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()) + .toBe('new http://link.'); + }); + it('should work with the target property', function() { + expect(element(by.id('linky-target')). + element(by.binding("snippetWithTarget | linky:'_blank'")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank'); + }); + + + */ + angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) { + var LINKY_URL_REGEXP = + /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/, + MAILTO_REGEXP = /^mailto:/; + + return function(text, target) { + if (!text) return text; + var match; + var raw = text; + var html = []; + var url; + var i; + while ((match = raw.match(LINKY_URL_REGEXP))) { + // We can not end in these as they are sometimes found at the end of the sentence + url = match[0]; + // if we did not match ftp/http/mailto then assume mailto + if (match[2] == match[3]) url = 'mailto:' + url; + i = match.index; + addText(raw.substr(0, i)); + addLink(url, match[0].replace(MAILTO_REGEXP, '')); + raw = raw.substring(i + match[0].length); + } + addText(raw); + return $sanitize(html.join('')); + + function addText(text) { + if (!text) { + return; + } + html.push(sanitizeText(text)); + } + + function addLink(url, text) { + html.push(''); + addText(text); + html.push(''); + } + }; + }]); + + +})(window, window.angular); \ No newline at end of file diff --git a/setup/pub/angular-sanitize/angular-sanitize.min.js.map b/setup/pub/angular-sanitize/angular-sanitize.min.js.map new file mode 100644 index 0000000000000..0c993a6ed2afb --- /dev/null +++ b/setup/pub/angular-sanitize/angular-sanitize.min.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"angular-sanitize.min.js", +"lineCount":13, +"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkBC,CAAlB,CAA6B,CAiJtCC,QAASA,EAAY,CAACC,CAAD,CAAQ,CAC3B,IAAIC,EAAM,EACGC,EAAAC,CAAmBF,CAAnBE,CAAwBN,CAAAO,KAAxBD,CACbH,MAAA,CAAaA,CAAb,CACA,OAAOC,EAAAI,KAAA,CAAS,EAAT,CAJoB,CAmE7BC,QAASA,EAAO,CAACC,CAAD,CAAM,CAAA,IAChBC,EAAM,EAAIC,EAAAA,CAAQF,CAAAG,MAAA,CAAU,GAAV,CAAtB,KAAsCC,CACtC,KAAKA,CAAL,CAAS,CAAT,CAAYA,CAAZ,CAAgBF,CAAAG,OAAhB,CAA8BD,CAAA,EAA9B,CAAmCH,CAAA,CAAIC,CAAA,CAAME,CAAN,CAAJ,CAAA,CAAgB,CAAA,CACnD,OAAOH,EAHa,CAmBtBK,QAASA,EAAU,CAAEC,CAAF,CAAQC,CAAR,CAAkB,CAiFnCC,QAASA,EAAa,CAAEC,CAAF,CAAOC,CAAP,CAAgBC,CAAhB,CAAsBC,CAAtB,CAA8B,CAClDF,CAAA,CAAUrB,CAAAwB,UAAA,CAAkBH,CAAlB,CACV,IAAKI,CAAA,CAAeJ,CAAf,CAAL,CACE,IAAA,CAAQK,CAAAC,KAAA,EAAR,EAAwBC,CAAA,CAAgBF,CAAAC,KAAA,EAAhB,CAAxB,CAAA,CACEE,CAAA,CAAa,EAAb,CAAiBH,CAAAC,KAAA,EAAjB,CAICG,EAAA,CAAwBT,CAAxB,CAAL,EAA0CK,CAAAC,KAAA,EAA1C,EAA0DN,CAA1D,EACEQ,CAAA,CAAa,EAAb,CAAiBR,CAAjB,CAKF,EAFAE,CAEA,CAFQQ,CAAA,CAAcV,CAAd,CAER,EAFmC,CAAC,CAACE,CAErC,GACEG,CAAAM,KAAA,CAAYX,CAAZ,CAEF,KAAIY,EAAQ,EAEZX,EAAAY,QAAA,CAAaC,CAAb,CACE,QAAQ,CAACC,CAAD,CAAQC,CAAR,CAAcC,CAAd,CAAiCC,CAAjC,CAAoDC,CAApD,CAAmE,CAMzEP,CAAA,CAAMI,CAAN,CAAA,CAAcI,CAAA,CALFH,CAKE,EAJTC,CAIS,EAHTC,CAGS,EAFT,EAES,CAN2D,CAD7E,CASItB,EAAAwB,MAAJ,EAAmBxB,CAAAwB,MAAA,CAAerB,CAAf,CAAwBY,CAAxB,CAA+BV,CAA/B,CA5B+B,CA+BpDM,QAASA,EAAW,CAAET,CAAF,CAAOC,CAAP,CAAiB,CAAA,IAC/BsB,EAAM,CADyB,CACtB7B,CAEb,IADAO,CACA,CADUrB,CAAAwB,UAAA,CAAkBH,CAAlB,CACV,CAEE,IAAMsB,CAAN,CAAYjB,CAAAX,OAAZ,CAA2B,CAA3B,CAAqC,CAArC,EAA8B4B,CAA9B,EACOjB,CAAA,CAAOiB,CAAP,CADP,EACuBtB,CADvB,CAAwCsB,CAAA,EAAxC;AAIF,GAAY,CAAZ,EAAKA,CAAL,CAAgB,CAEd,IAAM7B,CAAN,CAAUY,CAAAX,OAAV,CAAyB,CAAzB,CAA4BD,CAA5B,EAAiC6B,CAAjC,CAAsC7B,CAAA,EAAtC,CACMI,CAAA0B,IAAJ,EAAiB1B,CAAA0B,IAAA,CAAalB,CAAA,CAAOZ,CAAP,CAAb,CAGnBY,EAAAX,OAAA,CAAe4B,CAND,CATmB,CAhHF,IAC/BE,CAD+B,CACxB1C,CADwB,CACVuB,EAAQ,EADE,CACEC,EAAOV,CAG5C,KAFAS,CAAAC,KAEA,CAFamB,QAAQ,EAAG,CAAE,MAAOpB,EAAA,CAAOA,CAAAX,OAAP,CAAsB,CAAtB,CAAT,CAExB,CAAQE,CAAR,CAAA,CAAe,CACbd,CAAA,CAAQ,CAAA,CAGR,IAAMuB,CAAAC,KAAA,EAAN,EAAuBoB,CAAA,CAAiBrB,CAAAC,KAAA,EAAjB,CAAvB,CAmDEV,CASA,CATOA,CAAAiB,QAAA,CAAiBc,MAAJ,CAAW,kBAAX,CAAgCtB,CAAAC,KAAA,EAAhC,CAA+C,QAA/C,CAAyD,GAAzD,CAAb,CACL,QAAQ,CAACsB,CAAD,CAAMC,CAAN,CAAW,CACjBA,CAAA,CAAOA,CAAAhB,QAAA,CAAaiB,CAAb,CAA6B,IAA7B,CAAAjB,QAAA,CAA2CkB,CAA3C,CAAyD,IAAzD,CAEHlC,EAAAf,MAAJ,EAAmBe,CAAAf,MAAA,CAAesC,CAAA,CAAeS,CAAf,CAAf,CAEnB,OAAO,EALU,CADd,CASP,CAAArB,CAAA,CAAa,EAAb,CAAiBH,CAAAC,KAAA,EAAjB,CA5DF,KAAyD,CAGvD,GAA8B,CAA9B,GAAKV,CAAAoC,QAAA,CAAa,SAAb,CAAL,CAEER,CAEA,CAFQ5B,CAAAoC,QAAA,CAAa,IAAb,CAAmB,CAAnB,CAER,CAAc,CAAd,EAAKR,CAAL,EAAmB5B,CAAAqC,YAAA,CAAiB,QAAjB,CAAwBT,CAAxB,CAAnB,GAAsDA,CAAtD,GACM3B,CAAAqC,QAEJ,EAFqBrC,CAAAqC,QAAA,CAAiBtC,CAAAuC,UAAA,CAAgB,CAAhB,CAAmBX,CAAnB,CAAjB,CAErB,CADA5B,CACA,CADOA,CAAAuC,UAAA,CAAgBX,CAAhB,CAAwB,CAAxB,CACP,CAAA1C,CAAA,CAAQ,CAAA,CAHV,CAJF,KAUO,IAAKsD,CAAAC,KAAA,CAAoBzC,CAApB,CAAL,CAGL,IAFAmB,CAEA,CAFQnB,CAAAmB,MAAA,CAAYqB,CAAZ,CAER,CACExC,CACA;AADOA,CAAAiB,QAAA,CAAcE,CAAA,CAAM,CAAN,CAAd,CAAyB,EAAzB,CACP,CAAAjC,CAAA,CAAQ,CAAA,CAFV,CAHK,IAQA,IAAKwD,CAAAD,KAAA,CAA4BzC,CAA5B,CAAL,CAGL,IAFAmB,CAEA,CAFQnB,CAAAmB,MAAA,CAAYwB,CAAZ,CAER,CACE3C,CAEA,CAFOA,CAAAuC,UAAA,CAAgBpB,CAAA,CAAM,CAAN,CAAArB,OAAhB,CAEP,CADAqB,CAAA,CAAM,CAAN,CAAAF,QAAA,CAAkB0B,CAAlB,CAAkC/B,CAAlC,CACA,CAAA1B,CAAA,CAAQ,CAAA,CAHV,CAHK,IAUK0D,EAAAH,KAAA,CAAsBzC,CAAtB,CAAL,GACLmB,CADK,CACGnB,CAAAmB,MAAA,CAAY0B,CAAZ,CADH,IAIH7C,CAEA,CAFOA,CAAAuC,UAAA,CAAgBpB,CAAA,CAAM,CAAN,CAAArB,OAAhB,CAEP,CADAqB,CAAA,CAAM,CAAN,CAAAF,QAAA,CAAkB4B,CAAlB,CAAoC3C,CAApC,CACA,CAAAhB,CAAA,CAAQ,CAAA,CANL,CAUFA,EAAL,GACE0C,CAKA,CALQ5B,CAAAoC,QAAA,CAAa,GAAb,CAKR,CAHIH,CAGJ,CAHmB,CAAR,CAAAL,CAAA,CAAY5B,CAAZ,CAAmBA,CAAAuC,UAAA,CAAgB,CAAhB,CAAmBX,CAAnB,CAG9B,CAFA5B,CAEA,CAFe,CAAR,CAAA4B,CAAA,CAAY,EAAZ,CAAiB5B,CAAAuC,UAAA,CAAgBX,CAAhB,CAExB,CAAI3B,CAAAf,MAAJ,EAAmBe,CAAAf,MAAA,CAAesC,CAAA,CAAeS,CAAf,CAAf,CANrB,CAzCuD,CA+DzD,GAAKjC,CAAL,EAAaU,CAAb,CACE,KAAMoC,EAAA,CAAgB,UAAhB,CAC4C9C,CAD5C,CAAN,CAGFU,CAAA,CAAOV,CAvEM,CA2EfY,CAAA,EA/EmC,CA2IrCY,QAASA,EAAc,CAACuB,CAAD,CAAQ,CAC7B,GAAI,CAACA,CAAL,CAAc,MAAO,EAIrB,KAAIC,EAAQC,CAAAC,KAAA,CAAaH,CAAb,CACRI,EAAAA,CAAcH,CAAA,CAAM,CAAN,CAClB,KAAII,EAAaJ,CAAA,CAAM,CAAN,CAEjB,IADIK,CACJ,CADcL,CAAA,CAAM,CAAN,CACd,CACEM,CAAAC,UAKA,CALoBF,CAAApC,QAAA,CAAgB,IAAhB,CAAqB,MAArB,CAKpB,CAAAoC,CAAA,CAAU,aAAA,EAAiBC,EAAjB,CACRA,CAAAE,YADQ,CACgBF,CAAAG,UAE5B,OAAON,EAAP,CAAqBE,CAArB,CAA+BD,CAlBF,CA4B/BM,QAASA,EAAc,CAACX,CAAD,CAAQ,CAC7B,MAAOA,EAAA9B,QAAA,CACG,IADH;AACS,OADT,CAAAA,QAAA,CAEG0C,CAFH,CAE4B,QAAQ,CAACZ,CAAD,CAAO,CAC9C,MAAO,IAAP,CAAcA,CAAAa,WAAA,CAAiB,CAAjB,CAAd,CAAoC,GADU,CAF3C,CAAA3C,QAAA,CAKG,IALH,CAKS,MALT,CAAAA,QAAA,CAMG,IANH,CAMS,MANT,CADsB,CAoB/B7B,QAASA,EAAkB,CAACD,CAAD,CAAM0E,CAAN,CAAmB,CAC5C,IAAIC,EAAS,CAAA,CAAb,CACIC,EAAMhF,CAAAiF,KAAA,CAAa7E,CAAb,CAAkBA,CAAA4B,KAAlB,CACV,OAAO,OACEU,QAAQ,CAACtB,CAAD,CAAMa,CAAN,CAAaV,CAAb,CAAmB,CAChCH,CAAA,CAAMpB,CAAAwB,UAAA,CAAkBJ,CAAlB,CACD2D,EAAAA,CAAL,EAAehC,CAAA,CAAgB3B,CAAhB,CAAf,GACE2D,CADF,CACW3D,CADX,CAGK2D,EAAL,EAAsC,CAAA,CAAtC,GAAeG,CAAA,CAAc9D,CAAd,CAAf,GACE4D,CAAA,CAAI,GAAJ,CAcA,CAbAA,CAAA,CAAI5D,CAAJ,CAaA,CAZApB,CAAAmF,QAAA,CAAgBlD,CAAhB,CAAuB,QAAQ,CAAC+B,CAAD,CAAQoB,CAAR,CAAY,CACzC,IAAIC,EAAKrF,CAAAwB,UAAA,CAAkB4D,CAAlB,CAAT,CACIE,EAAmB,KAAnBA,GAAWlE,CAAXkE,EAAqC,KAArCA,GAA4BD,CAA5BC,EAAyD,YAAzDA,GAAgDD,CAC3B,EAAA,CAAzB,GAAIE,CAAA,CAAWF,CAAX,CAAJ,EACsB,CAAA,CADtB,GACGG,CAAA,CAASH,CAAT,CADH,EAC8B,CAAAP,CAAA,CAAad,CAAb,CAAoBsB,CAApB,CAD9B,GAEEN,CAAA,CAAI,GAAJ,CAIA,CAHAA,CAAA,CAAII,CAAJ,CAGA,CAFAJ,CAAA,CAAI,IAAJ,CAEA,CADAA,CAAA,CAAIL,CAAA,CAAeX,CAAf,CAAJ,CACA,CAAAgB,CAAA,CAAI,GAAJ,CANF,CAHyC,CAA3C,CAYA,CAAAA,CAAA,CAAIzD,CAAA,CAAQ,IAAR,CAAe,GAAnB,CAfF,CALgC,CAD7B,KAwBAqB,QAAQ,CAACxB,CAAD,CAAK,CACdA,CAAA,CAAMpB,CAAAwB,UAAA,CAAkBJ,CAAlB,CACD2D,EAAL,EAAsC,CAAA,CAAtC,GAAeG,CAAA,CAAc9D,CAAd,CAAf,GACE4D,CAAA,CAAI,IAAJ,CAEA,CADAA,CAAA,CAAI5D,CAAJ,CACA,CAAA4D,CAAA,CAAI,GAAJ,CAHF,CAKI5D,EAAJ,EAAW2D,CAAX,GACEA,CADF,CACW,CAAA,CADX,CAPc,CAxBb,OAmCE5E,QAAQ,CAACA,CAAD,CAAO,CACb4E,CAAL;AACEC,CAAA,CAAIL,CAAA,CAAexE,CAAf,CAAJ,CAFgB,CAnCjB,CAHqC,CAha9C,IAAI4D,EAAkB/D,CAAAyF,SAAA,CAAiB,WAAjB,CAAtB,CAwJI3B,EACG,4FAzJP,CA0JEF,EAAiB,2BA1JnB,CA2JEzB,EAAc,yEA3JhB,CA4JE0B,EAAmB,IA5JrB,CA6JEF,EAAyB,SA7J3B,CA8JER,EAAiB,qBA9JnB,CA+JEM,EAAiB,qBA/JnB,CAgKEL,EAAe,yBAhKjB,CAkKEwB,EAA0B,gBAlK5B,CA2KI7C,EAAetB,CAAA,CAAQ,wBAAR,CAIfiF,EAAAA,CAA8BjF,CAAA,CAAQ,gDAAR,CAC9BkF,EAAAA,CAA+BlF,CAAA,CAAQ,OAAR,CADnC,KAEIqB,EAAyB9B,CAAA4F,OAAA,CAAe,EAAf,CACeD,CADf,CAEeD,CAFf,CAF7B,CAOIjE,EAAgBzB,CAAA4F,OAAA,CAAe,EAAf,CAAmBF,CAAnB,CAAgDjF,CAAA,CAAQ,4KAAR,CAAhD,CAPpB;AAYImB,EAAiB5B,CAAA4F,OAAA,CAAe,EAAf,CAAmBD,CAAnB,CAAiDlF,CAAA,CAAQ,2JAAR,CAAjD,CAZrB,CAkBIsC,EAAkBtC,CAAA,CAAQ,cAAR,CAlBtB,CAoBIyE,EAAgBlF,CAAA4F,OAAA,CAAe,EAAf,CACe7D,CADf,CAEeN,CAFf,CAGeG,CAHf,CAIeE,CAJf,CApBpB,CA2BI0D,EAAW/E,CAAA,CAAQ,0CAAR,CA3Bf,CA4BI8E,EAAavF,CAAA4F,OAAA,CAAe,EAAf,CAAmBJ,CAAnB,CAA6B/E,CAAA,CAC1C,ySAD0C,CAA7B,CA5BjB;AA0LI8D,EAAUsB,QAAAC,cAAA,CAAuB,KAAvB,CA1Ld,CA2LI5B,EAAU,wBAsGdlE,EAAA+F,OAAA,CAAe,YAAf,CAA6B,EAA7B,CAAAC,SAAA,CAA0C,WAA1C,CA7UAC,QAA0B,EAAG,CAC3B,IAAAC,KAAA,CAAY,CAAC,eAAD,CAAkB,QAAQ,CAACC,CAAD,CAAgB,CACpD,MAAO,SAAQ,CAAClF,CAAD,CAAO,CACpB,IAAIb,EAAM,EACVY,EAAA,CAAWC,CAAX,CAAiBZ,CAAA,CAAmBD,CAAnB,CAAwB,QAAQ,CAACgG,CAAD,CAAMd,CAAN,CAAe,CAC9D,MAAO,CAAC,SAAA5B,KAAA,CAAeyC,CAAA,CAAcC,CAAd,CAAmBd,CAAnB,CAAf,CADsD,CAA/C,CAAjB,CAGA,OAAOlF,EAAAI,KAAA,CAAS,EAAT,CALa,CAD8B,CAA1C,CADe,CA6U7B,CAuGAR,EAAA+F,OAAA,CAAe,YAAf,CAAAM,OAAA,CAAoC,OAApC,CAA6C,CAAC,WAAD,CAAc,QAAQ,CAACC,CAAD,CAAY,CAAA,IACzEC,EACE,mEAFuE,CAGzEC,EAAgB,UAEpB,OAAO,SAAQ,CAACtD,CAAD,CAAOuD,CAAP,CAAe,CAoB5BC,QAASA,EAAO,CAACxD,CAAD,CAAO,CAChBA,CAAL,EAGAjC,CAAAe,KAAA,CAAU9B,CAAA,CAAagD,CAAb,CAAV,CAJqB,CAOvByD,QAASA,EAAO,CAACC,CAAD,CAAM1D,CAAN,CAAY,CAC1BjC,CAAAe,KAAA,CAAU,KAAV,CACIhC,EAAA6G,UAAA,CAAkBJ,CAAlB,CAAJ;CACExF,CAAAe,KAAA,CAAU,UAAV,CAEA,CADAf,CAAAe,KAAA,CAAUyE,CAAV,CACA,CAAAxF,CAAAe,KAAA,CAAU,IAAV,CAHF,CAKAf,EAAAe,KAAA,CAAU,QAAV,CACAf,EAAAe,KAAA,CAAU4E,CAAV,CACA3F,EAAAe,KAAA,CAAU,IAAV,CACA0E,EAAA,CAAQxD,CAAR,CACAjC,EAAAe,KAAA,CAAU,MAAV,CAX0B,CA1B5B,GAAI,CAACkB,CAAL,CAAW,MAAOA,EAMlB,KALA,IAAId,CAAJ,CACI0E,EAAM5D,CADV,CAEIjC,EAAO,EAFX,CAGI2F,CAHJ,CAII9F,CACJ,CAAQsB,CAAR,CAAgB0E,CAAA1E,MAAA,CAAUmE,CAAV,CAAhB,CAAA,CAEEK,CAMA,CANMxE,CAAA,CAAM,CAAN,CAMN,CAJIA,CAAA,CAAM,CAAN,CAIJ,EAJgBA,CAAA,CAAM,CAAN,CAIhB,GAJ0BwE,CAI1B,CAJgC,SAIhC,CAJ4CA,CAI5C,EAHA9F,CAGA,CAHIsB,CAAAS,MAGJ,CAFA6D,CAAA,CAAQI,CAAAC,OAAA,CAAW,CAAX,CAAcjG,CAAd,CAAR,CAEA,CADA6F,CAAA,CAAQC,CAAR,CAAaxE,CAAA,CAAM,CAAN,CAAAF,QAAA,CAAiBsE,CAAjB,CAAgC,EAAhC,CAAb,CACA,CAAAM,CAAA,CAAMA,CAAAtD,UAAA,CAAc1C,CAAd,CAAkBsB,CAAA,CAAM,CAAN,CAAArB,OAAlB,CAER2F,EAAA,CAAQI,CAAR,CACA,OAAOR,EAAA,CAAUrF,CAAAT,KAAA,CAAU,EAAV,CAAV,CAlBqB,CAL+C,CAAlC,CAA7C,CAzjBsC,CAArC,CAAA,CA0mBET,MA1mBF,CA0mBUA,MAAAC,QA1mBV;", +"sources":["angular-sanitize.js"], +"names":["window","angular","undefined","sanitizeText","chars","buf","htmlSanitizeWriter","writer","noop","join","makeMap","str","obj","items","split","i","length","htmlParser","html","handler","parseStartTag","tag","tagName","rest","unary","lowercase","blockElements","stack","last","inlineElements","parseEndTag","optionalEndTagElements","voidElements","push","attrs","replace","ATTR_REGEXP","match","name","doubleQuotedValue","singleQuotedValue","unquotedValue","decodeEntities","start","pos","end","index","stack.last","specialElements","RegExp","all","text","COMMENT_REGEXP","CDATA_REGEXP","indexOf","lastIndexOf","comment","substring","DOCTYPE_REGEXP","test","BEGING_END_TAGE_REGEXP","END_TAG_REGEXP","BEGIN_TAG_REGEXP","START_TAG_REGEXP","$sanitizeMinErr","value","parts","spaceRe","exec","spaceBefore","spaceAfter","content","hiddenPre","innerHTML","textContent","innerText","encodeEntities","NON_ALPHANUMERIC_REGEXP","charCodeAt","uriValidator","ignore","out","bind","validElements","forEach","key","lkey","isImage","validAttrs","uriAttrs","$$minErr","optionalEndTagBlockElements","optionalEndTagInlineElements","extend","document","createElement","module","provider","$SanitizeProvider","$get","$$sanitizeUri","uri","filter","$sanitize","LINKY_URL_REGEXP","MAILTO_REGEXP","target","addText","addLink","url","isDefined","raw","substr"] +} \ No newline at end of file diff --git a/setup/pub/magento/setup/add-database.js b/setup/pub/magento/setup/add-database.js index 841d8e81ccdf9..e255483f4afc6 100644 --- a/setup/pub/magento/setup/add-database.js +++ b/setup/pub/magento/setup/add-database.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/app.js b/setup/pub/magento/setup/app.js index ec890084324a8..c74f4a1551a96 100644 --- a/setup/pub/magento/setup/app.js +++ b/setup/pub/magento/setup/app.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/auth-dialog.js b/setup/pub/magento/setup/auth-dialog.js index b2b905120a3f8..9e2fcf0537d86 100644 --- a/setup/pub/magento/setup/auth-dialog.js +++ b/setup/pub/magento/setup/auth-dialog.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/complete-backup.js b/setup/pub/magento/setup/complete-backup.js index d1d404853eb05..19062731a7a4c 100644 --- a/setup/pub/magento/setup/complete-backup.js +++ b/setup/pub/magento/setup/complete-backup.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/create-admin-account.js b/setup/pub/magento/setup/create-admin-account.js index dd8357dd9a568..905406110684e 100644 --- a/setup/pub/magento/setup/create-admin-account.js +++ b/setup/pub/magento/setup/create-admin-account.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/create-backup.js b/setup/pub/magento/setup/create-backup.js index 2ff55e4087d00..b4a56d42b8278 100644 --- a/setup/pub/magento/setup/create-backup.js +++ b/setup/pub/magento/setup/create-backup.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/customize-your-store.js b/setup/pub/magento/setup/customize-your-store.js index bcec183f34a0c..9242f59b3a7dc 100644 --- a/setup/pub/magento/setup/customize-your-store.js +++ b/setup/pub/magento/setup/customize-your-store.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/data-option.js b/setup/pub/magento/setup/data-option.js index b90d42e2dc840..ca003ba3f9321 100644 --- a/setup/pub/magento/setup/data-option.js +++ b/setup/pub/magento/setup/data-option.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -25,4 +25,4 @@ angular.module('data-option', ['ngStorage']) $scope.$watch('component.dataOption', function(value) { $localStorage.dataOption = value; }); - }]); \ No newline at end of file + }]); diff --git a/setup/pub/magento/setup/extension-grid.js b/setup/pub/magento/setup/extension-grid.js index e5fc525dbd1bc..e657a0955b3d3 100644 --- a/setup/pub/magento/setup/extension-grid.js +++ b/setup/pub/magento/setup/extension-grid.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/home.js b/setup/pub/magento/setup/home.js index 3c72130be860c..7959eb3bcf5d7 100644 --- a/setup/pub/magento/setup/home.js +++ b/setup/pub/magento/setup/home.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/install-extension-grid.js b/setup/pub/magento/setup/install-extension-grid.js index 0ec3b66911f10..964c4f9dd3f21 100644 --- a/setup/pub/magento/setup/install-extension-grid.js +++ b/setup/pub/magento/setup/install-extension-grid.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/install.js b/setup/pub/magento/setup/install.js index e1c7c0a125336..47c82736bb622 100644 --- a/setup/pub/magento/setup/install.js +++ b/setup/pub/magento/setup/install.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/landing.js b/setup/pub/magento/setup/landing.js index 31ef19adb6b40..5c508a65cdbcc 100644 --- a/setup/pub/magento/setup/landing.js +++ b/setup/pub/magento/setup/landing.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/main.js b/setup/pub/magento/setup/main.js index 3f359df3232d7..e307319088c26 100644 --- a/setup/pub/magento/setup/main.js +++ b/setup/pub/magento/setup/main.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/marketplace-credentials.js b/setup/pub/magento/setup/marketplace-credentials.js index ec7bb6d254873..f65bee361e9b3 100644 --- a/setup/pub/magento/setup/marketplace-credentials.js +++ b/setup/pub/magento/setup/marketplace-credentials.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/module-grid.js b/setup/pub/magento/setup/module-grid.js index 21dec8795f135..8c5b0c48ba670 100644 --- a/setup/pub/magento/setup/module-grid.js +++ b/setup/pub/magento/setup/module-grid.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/readiness-check.js b/setup/pub/magento/setup/readiness-check.js index 1b89ea2be06a5..ae84cd10fdbef 100644 --- a/setup/pub/magento/setup/readiness-check.js +++ b/setup/pub/magento/setup/readiness-check.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/remove-dialog.js b/setup/pub/magento/setup/remove-dialog.js index 96a63ec5193b2..6ce02ba511128 100644 --- a/setup/pub/magento/setup/remove-dialog.js +++ b/setup/pub/magento/setup/remove-dialog.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/select-version.js b/setup/pub/magento/setup/select-version.js index 6a6ecdd9a99f1..dbfc315437ad5 100644 --- a/setup/pub/magento/setup/select-version.js +++ b/setup/pub/magento/setup/select-version.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/start-updater.js b/setup/pub/magento/setup/start-updater.js index 5b90c5333cdd1..e1ed53bab3f70 100644 --- a/setup/pub/magento/setup/start-updater.js +++ b/setup/pub/magento/setup/start-updater.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/success.js b/setup/pub/magento/setup/success.js index ed23aa8c37133..df06d5f89470c 100644 --- a/setup/pub/magento/setup/success.js +++ b/setup/pub/magento/setup/success.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/system-config.js b/setup/pub/magento/setup/system-config.js index b8f6f16725802..bb2a2d0612c19 100644 --- a/setup/pub/magento/setup/system-config.js +++ b/setup/pub/magento/setup/system-config.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/update-extension-grid.js b/setup/pub/magento/setup/update-extension-grid.js index 8f7149bf1645e..4450444972e5d 100644 --- a/setup/pub/magento/setup/update-extension-grid.js +++ b/setup/pub/magento/setup/update-extension-grid.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/updater-success.js b/setup/pub/magento/setup/updater-success.js index 36e58958ed691..0f44d1a04cee5 100644 --- a/setup/pub/magento/setup/updater-success.js +++ b/setup/pub/magento/setup/updater-success.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/pub/magento/setup/web-configuration.js b/setup/pub/magento/setup/web-configuration.js index 03a0fc7845dda..63dec9ead2aab 100644 --- a/setup/pub/magento/setup/web-configuration.js +++ b/setup/pub/magento/setup/web-configuration.js @@ -1,11 +1,11 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ 'use strict'; angular.module('web-configuration', ['ngStorage']) - .controller('webConfigurationController', ['$scope', '$state', '$localStorage', function ($scope, $state, $localStorage) { + .controller('webConfigurationController', ['$scope', '$state', '$localStorage', '$http', function ($scope, $state, $localStorage, $http) { $scope.config = { address: { base_url: '', @@ -119,4 +119,28 @@ angular.module('web-configuration', ['ngStorage']) $scope.webconfig.submitted = false; } }); + + // Validate URL + $scope.validateUrl = function () { + if (!$scope.webconfig.submitted) { + $http.post('index.php/url-check', $scope.config) + .success(function (data) { + $scope.validateUrl.result = data; + if ($scope.validateUrl.result.successUrl && $scope.validateUrl.result.successSecureUrl) { + $scope.nextState(); + } + if (!$scope.validateUrl.result.successUrl) { + $scope.webconfig.submitted = true; + $scope.webconfig.base_url.$setValidity('url', false); + } + if (!$scope.validateUrl.result.successSecureUrl) { + $scope.webconfig.submitted = true; + $scope.webconfig.https.$setValidity('url', false); + } + }) + .error(function (data) { + $scope.validateUrl.failed = data; + }); + } + }; }]); diff --git a/setup/pub/styles/setup.css b/setup/pub/styles/setup.css index 61893c04c3f16..980e8233a0950 100644 --- a/setup/pub/styles/setup.css +++ b/setup/pub/styles/setup.css @@ -1,6 +1,6 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -.abs-action-delete,.abs-icon,.action-close:before,.action-next:before,.action-previous:before,.admin-user .admin__action-dropdown:before,.admin__action-multiselect-dropdown:before,.admin__action-multiselect-search-label:before,.admin__control-checkbox+label:before,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete:before,.admin__control-table .action-delete:before,.admin__current-filters-list .action-remove:before,.admin__data-grid-action-bookmarks .action-delete:before,.admin__data-grid-action-bookmarks .action-edit:before,.admin__data-grid-action-bookmarks .action-submit:before,.admin__data-grid-action-bookmarks .admin__action-dropdown:before,.admin__data-grid-action-columns .admin__action-dropdown:before,.admin__data-grid-action-export .admin__action-dropdown:before,.admin__field-fallback-reset:before,.admin__menu .level-0>a:before,.admin__page-nav-item-message .admin__page-nav-item-message-icon,.admin__page-nav-title._collapsible:after,.data-grid-filters-action-wrap .action-default:before,.data-grid-row-changed:after,.data-grid-row-parent>td .data-grid-checkbox-cell-inner:before,.data-grid-search-control-wrap .action-submit:before,.extensions-information .list .extension-delete,.icon-failed:before,.icon-success:before,.notifications-action:before,.notifications-close:before,.page-actions .page-actions-buttons>button.action-back:before,.page-actions .page-actions-buttons>button.back:before,.page-actions>button.action-back:before,.page-actions>button.back:before,.page-title-jumbo-success:before,.search-global-label:before,.selectmenu .action-delete:before,.selectmenu .action-edit:before,.selectmenu .action-save:before,.setup-home-item:before,.sticky-header .data-grid-search-control-wrap .data-grid-search-label:before,.store-switcher .dropdown-menu .dropdown-toolbar a:before,.tooltip .help a:before,.tooltip .help span:before{-webkit-font-smoothing:antialiased;font-family:Icons;font-style:normal;font-weight:400;line-height:1;speak:none}.validation-symbol:after{color:#e22626;content:'*';font-weight:400;margin-left:3px}.abs-modal-overlay,.modals-overlay{background:rgba(0,0,0,.35);bottom:0;left:0;position:fixed;right:0;top:0}.abs-action-delete>span,.abs-visually-hidden,.action-multicheck-wrap .action-multicheck-toggle>span,.admin__actions-switch-checkbox,.admin__control-fields .admin__field:nth-child(n+2):not(.admin__field-option):not(.admin__field-group-show-label)>.admin__field-label,.admin__field-tooltip .admin__field-tooltip-action span,.customize-your-store .customize-your-store-default .legend,.extensions-information .list .extension-delete>span,.form-el-checkbox,.form-el-radio,.selectmenu .action-delete>span,.selectmenu .action-edit>span,.selectmenu .action-save>span,.selectmenu-toggle span,.tooltip .help a span,.tooltip .help span span,[class*=admin__control-grouped]>.admin__field:nth-child(n+2):not(.admin__field-option):not(.admin__field-group-show-label):not(.admin__field-date)>.admin__field-label{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.abs-visually-hidden-reset,.admin__field-group-columns>.admin__field:nth-child(n+2):not(.admin__field-option):not(.admin__field-group-show-label):not(.admin__field-date)>.admin__field-label[class]{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.abs-clearfix:after,.abs-clearfix:before,.action-multicheck-wrap:after,.action-multicheck-wrap:before,.actions-split:after,.actions-split:before,.admin__control-table-pagination:after,.admin__control-table-pagination:before,.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-content:after,.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-content:before,.admin__data-grid-filters-footer:after,.admin__data-grid-filters-footer:before,.admin__data-grid-filters:after,.admin__data-grid-filters:before,.admin__data-grid-header-row:after,.admin__data-grid-header-row:before,.admin__field-complex:after,.admin__field-complex:before,.modal-slide .magento-message .insert-title-inner:after,.modal-slide .magento-message .insert-title-inner:before,.modal-slide .main-col .insert-title-inner:after,.modal-slide .main-col .insert-title-inner:before,.page-actions._fixed:after,.page-actions._fixed:before,.page-content:after,.page-content:before,.page-header-actions:after,.page-header-actions:before,.page-main-actions:not(._hidden):after,.page-main-actions:not(._hidden):before{content:'';display:table}.abs-clearfix:after,.action-multicheck-wrap:after,.actions-split:after,.admin__control-table-pagination:after,.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-content:after,.admin__data-grid-filters-footer:after,.admin__data-grid-filters:after,.admin__data-grid-header-row:after,.admin__field-complex:after,.modal-slide .magento-message .insert-title-inner:after,.modal-slide .main-col .insert-title-inner:after,.page-actions._fixed:after,.page-content:after,.page-header-actions:after,.page-main-actions:not(._hidden):after{clear:both}.abs-list-reset-styles{margin:0;padding:0;list-style:none}.abs-draggable-handle,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .draggable-handle,.admin__control-table .draggable-handle,.data-grid .data-grid-draggable-row-cell .draggable-handle{cursor:-webkit-grab;cursor:move;font-size:0;margin-top:-4px;padding:0 1rem 0 0;vertical-align:middle;display:inline-block;text-decoration:none}.abs-draggable-handle:before,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .draggable-handle:before,.admin__control-table .draggable-handle:before,.data-grid .data-grid-draggable-row-cell .draggable-handle:before{-webkit-font-smoothing:antialiased;font-size:1.8rem;line-height:inherit;color:#9e9e9e;content:'\e617';font-family:Icons;vertical-align:middle;display:inline-block;font-weight:400;overflow:hidden;speak:none;text-align:center}.abs-draggable-handle:hover:before,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .draggable-handle:hover:before,.admin__control-table .draggable-handle:hover:before,.data-grid .data-grid-draggable-row-cell .draggable-handle:hover:before{color:#858585}.abs-config-scope-label,.admin__field:not(.admin__field-option)>.admin__field-label span[data-config-scope]:before{bottom:-1.3rem;color:gray;content:attr(data-config-scope);font-size:1.1rem;font-weight:400;min-width:15rem;position:absolute;right:0;text-transform:lowercase}.abs-word-wrap,.admin__field:not(.admin__field-option)>.admin__field-label{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;box-sizing:border-box}*,:after,:before{box-sizing:inherit}:focus{box-shadow:none;outline:0}._keyfocus :focus{box-shadow:0 0 0 1px #008bdb}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}mark{background:#ff0;color:#000}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}embed,img,object,video{max-width:100%}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@font-face{font-family:'Open Sans';src:url(../fonts/opensans/light/opensans-300.eot);src:url(../fonts/opensans/light/opensans-300.eot?#iefix) format('embedded-opentype'),url(../fonts/opensans/light/opensans-300.woff2) format('woff2'),url(../fonts/opensans/light/opensans-300.woff) format('woff'),url(../fonts/opensans/light/opensans-300.ttf) format('truetype'),url('../fonts/opensans/light/opensans-300.svg#Open Sans') format('svg');font-weight:300;font-style:normal}@font-face{font-family:'Open Sans';src:url(../fonts/opensans/regular/opensans-400.eot);src:url(../fonts/opensans/regular/opensans-400.eot?#iefix) format('embedded-opentype'),url(../fonts/opensans/regular/opensans-400.woff2) format('woff2'),url(../fonts/opensans/regular/opensans-400.woff) format('woff'),url(../fonts/opensans/regular/opensans-400.ttf) format('truetype'),url('../fonts/opensans/regular/opensans-400.svg#Open Sans') format('svg');font-weight:400;font-style:normal}@font-face{font-family:'Open Sans';src:url(../fonts/opensans/semibold/opensans-600.eot);src:url(../fonts/opensans/semibold/opensans-600.eot?#iefix) format('embedded-opentype'),url(../fonts/opensans/semibold/opensans-600.woff2) format('woff2'),url(../fonts/opensans/semibold/opensans-600.woff) format('woff'),url(../fonts/opensans/semibold/opensans-600.ttf) format('truetype'),url('../fonts/opensans/semibold/opensans-600.svg#Open Sans') format('svg');font-weight:600;font-style:normal}@font-face{font-family:'Open Sans';src:url(../fonts/opensans/bold/opensans-700.eot);src:url(../fonts/opensans/bold/opensans-700.eot?#iefix) format('embedded-opentype'),url(../fonts/opensans/bold/opensans-700.woff2) format('woff2'),url(../fonts/opensans/bold/opensans-700.woff) format('woff'),url(../fonts/opensans/bold/opensans-700.ttf) format('truetype'),url('../fonts/opensans/bold/opensans-700.svg#Open Sans') format('svg');font-weight:700;font-style:normal}html{font-size:62.5%}body{color:#333;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.36;font-size:1.4rem}h1{margin:0 0 2rem;color:#41362f;font-weight:400;line-height:1.2;font-size:2.8rem}h2{margin:0 0 2rem;color:#41362f;font-weight:400;line-height:1.2;font-size:2rem}h3{margin:0 0 2rem;color:#41362f;font-weight:600;line-height:1.2;font-size:1.7rem}h4,h5,h6{font-weight:600;margin-top:0}p{margin:0 0 1em}small{font-size:1.2rem}a{color:#008bdb;text-decoration:none}a:hover{color:#0fa7ff;text-decoration:underline}dl,ol,ul{padding-left:0}nav ol,nav ul{list-style:none;margin:0;padding:0}html{height:100%}body{background-color:#fff;min-height:100%;min-width:102.4rem}.page-wrapper{background-color:#fff;display:inline-block;margin-left:-4px;vertical-align:top;width:calc(100% - 8.8rem)}.page-content{padding-bottom:3rem;padding-left:3rem;padding-right:3rem}.notices-wrapper{margin:0 3rem}.notices-wrapper .messages{margin-bottom:0}.row{margin-left:0;margin-right:0}.row:after{clear:both;content:'';display:table}.col-l-1,.col-l-10,.col-l-11,.col-l-12,.col-l-2,.col-l-3,.col-l-4,.col-l-5,.col-l-6,.col-l-7,.col-l-8,.col-l-9,.col-m-1,.col-m-10,.col-m-11,.col-m-12,.col-m-2,.col-m-3,.col-m-4,.col-m-5,.col-m-6,.col-m-7,.col-m-8,.col-m-9,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{min-height:1px;padding-left:0;padding-right:0;position:relative}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}.row-gutter{margin-left:-1.5rem;margin-right:-1.5rem}.row-gutter>[class*=col-]{padding-left:1.5rem;padding-right:1.5rem}.abs-clearer:after,.extension-manager-content:after,.extension-manager-title:after,.form-row:after,.header:after,.nav:after,body:after{clear:both;content:'';display:table}.ng-cloak{display:none!important}.hide.hide{display:none}.show.show{display:block}.text-center{text-align:center}.text-right{text-align:right}@font-face{font-family:Icons;src:url(../fonts/icons/icons.eot);src:url(../fonts/icons/icons.eot?#iefix) format('embedded-opentype'),url(../fonts/icons/icons.woff2) format('woff2'),url(../fonts/icons/icons.woff) format('woff'),url(../fonts/icons/icons.ttf) format('truetype'),url(../fonts/icons/icons.svg#Icons) format('svg');font-weight:400;font-style:normal}[class*=icon-]{display:inline-block;line-height:1}.icon-failed:before,.icon-success:before,[class*=icon-]:after{font-family:Icons}.icon-success{color:#79a22e}.icon-success:before{content:'\e62d'}.icon-failed{color:#e22626}.icon-failed:before{content:'\e632'}.icon-success-thick:after{content:'\e62d'}.icon-collapse:after{content:'\e615'}.icon-failed-thick:after{content:'\e632'}.icon-expand:after{content:'\e616'}.icon-warning:after{content:'\e623'}.icon-failed-round,.icon-success-round{border-radius:100%;color:#fff;font-size:2.5rem;height:1em;position:relative;text-align:center;width:1em}.icon-failed-round:after,.icon-success-round:after{bottom:0;font-size:.5em;left:0;position:absolute;right:0;top:.45em}.icon-success-round{background-color:#79a22e}.icon-success-round:after{content:'\e62d'}.icon-failed-round{background-color:#e22626}.icon-failed-round:after{content:'\e632'}dl,ol,ul{margin-top:0}.list{padding-left:0}.list>li{display:block;margin-bottom:.75em;position:relative}.list>li>.icon-failed,.list>li>.icon-success{font-size:1.6em;left:-.1em;position:absolute;top:0}.list>li>.icon-success{color:#79a22e}.list>li>.icon-failed{color:#e22626}.list-item-failed,.list-item-icon,.list-item-success,.list-item-warning{padding-left:3.5rem}.list-item-failed:before,.list-item-success:before,.list-item-warning:before{left:-.1em;position:absolute}.list-item-success:before{color:#79a22e}.list-item-failed:before{color:#e22626}.list-item-warning:before{color:#ef672f}.list-definition{margin:0 0 3rem;padding:0}.list-definition>dt{clear:left;float:left}.list-definition>dd{margin-bottom:1em;margin-left:20rem}.btn-wrap{margin:0 auto}.btn-wrap .btn{width:100%}.btn{background:#e3e3e3;border:none;color:#514943;display:inline-block;font-size:1.6rem;font-weight:600;padding:.45em .9em;text-align:center}.btn:hover{background-color:#dbdbdb;color:#514943;text-decoration:none}.btn:active{background-color:#d6d6d6}.btn.disabled,.btn[disabled]{cursor:default;opacity:.5;pointer-events:none}.ie9 .btn.disabled,.ie9 .btn[disabled]{background-color:#f0f0f0;opacity:1;text-shadow:none}.btn-large{padding:.75em 1.25em}.btn-medium{font-size:1.4rem;padding:.5em 1.5em .6em}.btn-link{background-color:transparent;border:none;color:#008bdb;font-family:1.6rem;font-size:1.5rem}.btn-link:active,.btn-link:focus,.btn-link:hover{background-color:transparent;color:#0fa7ff}.btn-prime{background-color:#eb5202;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.25)}.btn-prime:focus,.btn-prime:hover{background-color:#f65405;background-repeat:repeat-x;background-image:linear-gradient(to right,#e04f00 0,#f65405 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e04f00', endColorstr='#f65405', GradientType=1);color:#fff}.btn-prime:active{background-color:#e04f00;background-repeat:repeat-x;background-image:linear-gradient(to right,#f65405 0,#e04f00 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f65405', endColorstr='#e04f00', GradientType=1);color:#fff}.ie9 .btn-prime.disabled,.ie9 .btn-prime[disabled]{background-color:#fd6e23}.ie9 .btn-prime.disabled:active,.ie9 .btn-prime.disabled:hover,.ie9 .btn-prime[disabled]:active,.ie9 .btn-prime[disabled]:hover{background-color:#fd6e23;-webkit-filter:none;filter:none}.btn-secondary{background-color:#514943;color:#fff}.btn-secondary:hover{background-color:#5f564f;color:#fff}.btn-secondary:active,.btn-secondary:focus{background-color:#574e48;color:#fff}.ie9 .btn-secondary.disabled,.ie9 .btn-secondary[disabled]{background-color:#514943}.ie9 .btn-secondary.disabled:active,.ie9 .btn-secondary[disabled]:active{background-color:#514943;-webkit-filter:none;filter:none}[class*=btn-wrap-triangle]{overflow:hidden;position:relative}[class*=btn-wrap-triangle] .btn:after{border-style:solid;content:'';height:0;position:absolute;top:0;width:0}.btn-wrap-triangle-right{display:inline-block;padding-right:1.74rem;position:relative}.btn-wrap-triangle-right .btn{text-indent:.92rem}.btn-wrap-triangle-right .btn:after{border-color:transparent transparent transparent #e3e3e3;border-width:1.84rem 0 1.84rem 1.84rem;left:100%;margin-left:-1.74rem}.btn-wrap-triangle-right .btn:focus:after,.btn-wrap-triangle-right .btn:hover:after{border-left-color:#dbdbdb}.btn-wrap-triangle-right .btn:active:after{border-left-color:#d6d6d6}.btn-wrap-triangle-right .btn:not(.disabled):active,.btn-wrap-triangle-right .btn:not([disabled]):active{left:1px}.ie9 .btn-wrap-triangle-right .btn.disabled:after,.ie9 .btn-wrap-triangle-right .btn[disabled]:after{border-color:transparent transparent transparent #f0f0f0}.ie9 .btn-wrap-triangle-right .btn.disabled:active:after,.ie9 .btn-wrap-triangle-right .btn.disabled:focus:after,.ie9 .btn-wrap-triangle-right .btn.disabled:hover:after,.ie9 .btn-wrap-triangle-right .btn[disabled]:active:after,.ie9 .btn-wrap-triangle-right .btn[disabled]:focus:after,.ie9 .btn-wrap-triangle-right .btn[disabled]:hover:after{border-left-color:#f0f0f0}.btn-wrap-triangle-right .btn-prime:after{border-color:transparent transparent transparent #eb5202}.btn-wrap-triangle-right .btn-prime:focus:after,.btn-wrap-triangle-right .btn-prime:hover:after{border-left-color:#f65405}.btn-wrap-triangle-right .btn-prime:active:after{border-left-color:#e04f00}.btn-wrap-triangle-right .btn-prime:not(.disabled):active,.btn-wrap-triangle-right .btn-prime:not([disabled]):active{left:1px}.ie9 .btn-wrap-triangle-right .btn-prime.disabled:after,.ie9 .btn-wrap-triangle-right .btn-prime[disabled]:after{border-color:transparent transparent transparent #fd6e23}.ie9 .btn-wrap-triangle-right .btn-prime.disabled:active:after,.ie9 .btn-wrap-triangle-right .btn-prime.disabled:hover:after,.ie9 .btn-wrap-triangle-right .btn-prime[disabled]:active:after,.ie9 .btn-wrap-triangle-right .btn-prime[disabled]:hover:after{border-left-color:#fd6e23}.btn-wrap-triangle-left{display:inline-block;padding-left:1.74rem}.btn-wrap-triangle-left .btn{text-indent:-.92rem}.btn-wrap-triangle-left .btn:after{border-color:transparent #e3e3e3 transparent transparent;border-width:1.84rem 1.84rem 1.84rem 0;margin-right:-1.74rem;right:100%}.btn-wrap-triangle-left .btn:focus:after,.btn-wrap-triangle-left .btn:hover:after{border-right-color:#dbdbdb}.btn-wrap-triangle-left .btn:active:after{border-right-color:#d6d6d6}.btn-wrap-triangle-left .btn:not(.disabled):active,.btn-wrap-triangle-left .btn:not([disabled]):active{right:1px}.ie9 .btn-wrap-triangle-left .btn.disabled:after,.ie9 .btn-wrap-triangle-left .btn[disabled]:after{border-color:transparent #f0f0f0 transparent transparent}.ie9 .btn-wrap-triangle-left .btn.disabled:active:after,.ie9 .btn-wrap-triangle-left .btn.disabled:hover:after,.ie9 .btn-wrap-triangle-left .btn[disabled]:active:after,.ie9 .btn-wrap-triangle-left .btn[disabled]:hover:after{border-right-color:#f0f0f0}.btn-wrap-triangle-left .btn-prime:after{border-color:transparent #eb5202 transparent transparent}.btn-wrap-triangle-left .btn-prime:focus:after,.btn-wrap-triangle-left .btn-prime:hover:after{border-right-color:#e04f00}.btn-wrap-triangle-left .btn-prime:active:after{border-right-color:#f65405}.btn-wrap-triangle-left .btn-prime:not(.disabled):active,.btn-wrap-triangle-left .btn-prime:not([disabled]):active{right:1px}.ie9 .btn-wrap-triangle-left .btn-prime.disabled:after,.ie9 .btn-wrap-triangle-left .btn-prime[disabled]:after{border-color:transparent #fd6e23 transparent transparent}.ie9 .btn-wrap-triangle-left .btn-prime.disabled:active:after,.ie9 .btn-wrap-triangle-left .btn-prime.disabled:hover:after,.ie9 .btn-wrap-triangle-left .btn-prime[disabled]:active:after,.ie9 .btn-wrap-triangle-left .btn-prime[disabled]:hover:after{border-right-color:#fd6e23}.btn-expand{background-color:transparent;border:none;color:#303030;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.4rem;font-weight:700;padding:0;position:relative}.btn-expand.expanded:after{border-color:transparent transparent #303030;border-width:0 .285em .36em}.btn-expand.expanded:hover:after{border-color:transparent transparent #3d3d3d}.btn-expand:hover{background-color:transparent;border:none;color:#3d3d3d}.btn-expand:hover:after{border-color:#3d3d3d transparent transparent}.btn-expand:after{border-color:#303030 transparent transparent;border-style:solid;border-width:.36em .285em 0;content:'';height:0;left:100%;margin-left:.5em;margin-top:-.18em;position:absolute;top:50%;width:0}[class*=col-] .form-el-input,[class*=col-] .form-el-select{width:100%}.form-fieldset{border:none;margin:0 0 1em;padding:0}.form-row{margin-bottom:2.2rem}.form-row .form-row{margin-bottom:.4rem}.form-row .form-label{display:block;font-weight:600;padding:.6rem 2.1em 0 0;text-align:right}.form-row .form-label.required{position:relative}.form-row .form-label.required:after{color:#eb5202;content:'*';font-size:1.15em;position:absolute;right:.7em;top:.5em}.form-row .form-el-checkbox+.form-label:before,.form-row .form-el-radio+.form-label:before{top:.7rem}.form-row .form-el-checkbox+.form-label:after,.form-row .form-el-radio+.form-label:after{top:1.1rem}.form-row.form-row-text{padding-top:.6rem}.form-row.form-row-text .action-sign-out{font-size:1.2rem;margin-left:1rem}.form-note{font-size:1.2rem;font-weight:600;margin-top:1rem}.form-el-dummy{display:none}.fieldset{border:0;margin:0;min-width:0;padding:0}input:not([disabled]):focus,textarea:not([disabled]):focus{box-shadow:none}.form-el-input{border:1px solid #adadad;color:#303030;padding:.35em .55em .5em}.form-el-input:hover{border-color:#949494}.form-el-input:focus{border-color:#008bdb}.form-el-input:required{box-shadow:none}.form-label{margin-bottom:.5em}[class*=form-label][for]{cursor:pointer}.form-el-insider-wrap{display:table;width:100%}.form-el-insider-input{display:table-cell;width:100%}.form-el-insider{border-radius:2px;display:table-cell;padding:.43em .55em .5em 0;vertical-align:top}.form-legend,.form-legend-expand,.form-legend-light{display:block;margin:0}.form-legend,.form-legend-expand{font-size:1.25em;font-weight:600;margin-bottom:2.5em;padding-top:1.5em}.form-legend{border-top:1px solid #ccc;width:100%}.form-legend-light{font-size:1em;margin-bottom:1.5em}.form-legend-expand{cursor:pointer;transition:opacity .2s linear}.form-legend-expand:hover{opacity:.85}.form-legend-expand.expanded:after{content:'\e615'}.form-legend-expand:after{content:'\e616';font-family:Icons;font-size:1.15em;font-weight:400;margin-left:.5em;vertical-align:sub}.form-el-checkbox.disabled+.form-label,.form-el-checkbox.disabled+.form-label:before,.form-el-checkbox[disabled]+.form-label,.form-el-checkbox[disabled]+.form-label:before,.form-el-radio.disabled+.form-label,.form-el-radio.disabled+.form-label:before,.form-el-radio[disabled]+.form-label,.form-el-radio[disabled]+.form-label:before{cursor:default;opacity:.5;pointer-events:none}.form-el-checkbox:not(.disabled)+.form-label:hover:before,.form-el-checkbox:not([disabled])+.form-label:hover:before,.form-el-radio:not(.disabled)+.form-label:hover:before,.form-el-radio:not([disabled])+.form-label:hover:before{border-color:#514943}.form-el-checkbox+.form-label,.form-el-radio+.form-label{font-weight:400;padding-left:2em;padding-right:0;position:relative;text-align:left;transition:border-color .1s linear}.form-el-checkbox+.form-label:before,.form-el-radio+.form-label:before{border:1px solid;content:'';left:0;position:absolute;top:.1rem;transition:border-color .1s linear}.form-el-checkbox+.form-label:before{background-color:#fff;border-color:#adadad;border-radius:2px;font-size:1.2rem;height:1.6rem;line-height:1.2;width:1.6rem}.form-el-checkbox:checked+.form-label::before{content:'\e62d';font-family:Icons}.form-el-radio+.form-label:before{background-color:#fff;border:1px solid #adadad;border-radius:100%;height:1.8rem;width:1.8rem}.form-el-radio+.form-label:after{background:0 0;border:.5rem solid transparent;border-radius:100%;content:'';height:0;left:.4rem;position:absolute;top:.5rem;transition:background .3s linear;width:0}.form-el-radio:checked+.form-label{cursor:default}.form-el-radio:checked+.form-label:after{border-color:#514943}.form-select-label{border:1px solid #adadad;color:#303030;cursor:pointer;display:block;overflow:hidden;position:relative;z-index:0}.form-select-label:hover,.form-select-label:hover:after{border-color:#949494}.form-select-label:active,.form-select-label:active:after,.form-select-label:focus,.form-select-label:focus:after{border-color:#008bdb}.form-select-label:after{background:#e3e3e3;border-left:1px solid #adadad;bottom:0;content:'';position:absolute;right:0;top:0;width:2.36em;z-index:-2}.ie9 .form-select-label:after{display:none}.form-select-label:before{border-color:#303030 transparent transparent;border-style:solid;border-width:5px 4px 0;content:'';height:0;margin-right:-4px;margin-top:-2.5px;position:absolute;right:1.18em;top:50%;width:0;z-index:-1}.ie9 .form-select-label:before{display:none}.form-select-label .form-el-select{background:0 0;border:none;border-radius:0;content:'';display:block;margin:0;padding:.35em calc(2.36em + 10%) .5em .55em;width:110%}.ie9 .form-select-label .form-el-select{padding-right:.55em;width:100%}.form-select-label .form-el-select::-ms-expand{display:none}.form-el-select{background:#fff;border:1px solid #adadad;border-radius:2px;color:#303030;display:block;padding:.35em .55em}.multiselect-custom{border:1px solid #adadad;height:45.2rem;margin:0 0 1.5rem;overflow:auto;position:relative}.multiselect-custom ul{margin:0;padding:0;list-style:none;min-width:29rem}.multiselect-custom .item{padding:1rem 1.4rem}.multiselect-custom .selected{background-color:#e0f6fe}.multiselect-custom .form-label{margin-bottom:0}[class*=form-el-].invalid{border-color:#e22626}[class*=form-el-].invalid+.error-container{display:block}.error-container{background-color:#fffbbb;border:1px solid #ee7d7d;color:#514943;display:none;font-size:1.19rem;margin-top:.2rem;padding:.8rem 1rem .9rem}.check-result-message{margin-left:.5em;min-height:3.68rem;-ms-align-items:center;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex}.check-result-text{margin-left:.5em}body:not([class]){min-width:0}.container{display:block;margin:0 auto 4rem;max-width:100rem;padding:0}.abs-action-delete,.action-close:before,.action-next:before,.action-previous:before,.admin-user .admin__action-dropdown:before,.admin__action-multiselect-dropdown:before,.admin__action-multiselect-search-label:before,.admin__control-checkbox+label:before,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete:before,.admin__control-table .action-delete:before,.admin__current-filters-list .action-remove:before,.admin__data-grid-action-bookmarks .action-delete:before,.admin__data-grid-action-bookmarks .action-edit:before,.admin__data-grid-action-bookmarks .action-submit:before,.admin__data-grid-action-bookmarks .admin__action-dropdown:before,.admin__data-grid-action-columns .admin__action-dropdown:before,.admin__data-grid-action-export .admin__action-dropdown:before,.admin__field-fallback-reset:before,.admin__menu .level-0>a:before,.admin__page-nav-item-message .admin__page-nav-item-message-icon,.admin__page-nav-title._collapsible:after,.data-grid-filters-action-wrap .action-default:before,.data-grid-row-changed:after,.data-grid-row-parent>td .data-grid-checkbox-cell-inner:before,.data-grid-search-control-wrap .action-submit:before,.extensions-information .list .extension-delete,.icon-failed:before,.icon-success:before,.notifications-action:before,.notifications-close:before,.page-actions .page-actions-buttons>button.action-back:before,.page-actions .page-actions-buttons>button.back:before,.page-actions>button.action-back:before,.page-actions>button.back:before,.page-title-jumbo-success:before,.search-global-label:before,.selectmenu .action-delete:before,.selectmenu .action-edit:before,.selectmenu .action-save:before,.setup-home-item:before,.sticky-header .data-grid-search-control-wrap .data-grid-search-label:before,.store-switcher .dropdown-menu .dropdown-toolbar a:before,.tooltip .help a:before,.tooltip .help span:before{-webkit-font-smoothing:antialiased;font-family:Icons;font-style:normal;font-weight:400;line-height:1;speak:none}.text-stretch{margin-bottom:1.5em}.page-title-jumbo{font-size:4rem;font-weight:300;letter-spacing:-.05em;margin-bottom:2.9rem}.page-title-jumbo-success:before{color:#79a22e;content:'\e62d';font-size:3.9rem;margin-left:-.3rem;margin-right:2.4rem}.list{margin-bottom:3rem}.list-dot .list-item{display:list-item;list-style-position:inside;margin-bottom:1.2rem}.list-title{color:#333;font-size:1.4rem;font-weight:700;letter-spacing:.025em;margin-bottom:1.2rem}.list-item-failed:before,.list-item-success:before,.list-item-warning:before{font-family:Icons;font-size:1.6rem;top:0}.list-item-success:before{content:'\e62d';font-size:1.6rem}.list-item-failed:before{content:'\e632';font-size:1.4rem;left:.1rem;top:.2rem}.list-item-warning:before{content:'\e623';font-size:1.3rem;left:.2rem}.form-wrap{margin-bottom:3.6rem;padding-top:2.1rem}.form-el-label-horizontal{display:inline-block;font-size:1.3rem;font-weight:600;letter-spacing:.025em;margin-bottom:.4rem;margin-left:.4rem}.app-updater{min-width:768px}body._has-modal{height:100%;overflow:hidden;width:100%}.modals-overlay{z-index:899}.modal-popup,.modal-slide{bottom:0;min-width:0;position:fixed;right:0;top:0;visibility:hidden}.modal-popup._show,.modal-slide._show{visibility:visible}.modal-popup._show .modal-inner-wrap,.modal-slide._show .modal-inner-wrap{-ms-transform:translate(0,0);transform:translate(0,0)}.modal-popup .modal-inner-wrap,.modal-slide .modal-inner-wrap{background-color:#fff;box-shadow:0 0 12px 2px rgba(0,0,0,.35);opacity:1;pointer-events:auto}.modal-slide{left:14.8rem;z-index:900}.modal-slide._show .modal-inner-wrap{-ms-transform:translateX(0);transform:translateX(0)}.modal-slide .modal-inner-wrap{height:100%;overflow-y:auto;position:static;-ms-transform:translateX(100%);transform:translateX(100%);transition-duration:.3s;transition-property:transform,visibility;transition-timing-function:ease-in-out;width:auto}.modal-slide._inner-scroll .modal-inner-wrap{overflow-y:visible;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.modal-slide._inner-scroll .modal-footer,.modal-slide._inner-scroll .modal-header{-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.modal-slide._inner-scroll .modal-content{overflow-y:auto}.modal-slide._inner-scroll .modal-footer{margin-top:auto}.modal-slide .modal-content,.modal-slide .modal-footer,.modal-slide .modal-header{padding:0 2.6rem 2.6rem}.modal-slide .modal-header{padding-bottom:2.1rem;padding-top:2.1rem}.modal-popup{z-index:900;left:0;overflow-y:auto}.modal-popup._show .modal-inner-wrap{-ms-transform:translateY(0);transform:translateY(0)}.modal-popup .modal-inner-wrap{margin:5rem auto;width:75%;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;box-sizing:border-box;height:auto;left:0;position:absolute;right:0;-ms-transform:translateY(-200%);transform:translateY(-200%);transition-duration:.2s;transition-property:transform,visibility;transition-timing-function:ease}.modal-popup._inner-scroll{overflow-y:visible}.ie10 .modal-popup._inner-scroll,.ie9 .modal-popup._inner-scroll{overflow-y:auto}.modal-popup._inner-scroll .modal-inner-wrap{max-height:90%}.ie10 .modal-popup._inner-scroll .modal-inner-wrap,.ie9 .modal-popup._inner-scroll .modal-inner-wrap{max-height:none}.modal-popup._inner-scroll .modal-content{overflow-y:auto}.modal-popup .modal-content,.modal-popup .modal-footer,.modal-popup .modal-header{padding-left:3rem;padding-right:3rem}.modal-popup .modal-footer,.modal-popup .modal-header{-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.modal-popup .modal-header{padding-bottom:1.2rem;padding-top:3rem}.modal-popup .modal-footer{margin-top:auto;padding-bottom:3rem}.modal-popup .modal-footer-actions{text-align:right}.admin__action-dropdown-wrap{display:inline-block;position:relative}.admin__action-dropdown-wrap .admin__action-dropdown-text:after{left:-6px;right:0}.admin__action-dropdown-wrap .admin__action-dropdown-menu{left:auto;right:0}.admin__action-dropdown-wrap._active .admin__action-dropdown,.admin__action-dropdown-wrap.active .admin__action-dropdown{border-color:#007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5)}.admin__action-dropdown-wrap._active .admin__action-dropdown-text:after,.admin__action-dropdown-wrap.active .admin__action-dropdown-text:after{background-color:#fff;content:'';height:6px;position:absolute;top:100%}.admin__action-dropdown-wrap._active .admin__action-dropdown-menu,.admin__action-dropdown-wrap.active .admin__action-dropdown-menu{display:block}.admin__action-dropdown-wrap._disabled .admin__action-dropdown{cursor:default}.admin__action-dropdown-wrap._disabled:hover .admin__action-dropdown{color:#333}.admin__action-dropdown{background-color:#fff;border:1px solid transparent;border-bottom:none;border-radius:0;box-shadow:none;color:#333;display:inline-block;font-size:1.3rem;font-weight:400;letter-spacing:-.025em;padding:.7rem 3.3rem .8rem 1.5rem;position:relative;vertical-align:baseline;z-index:2}.admin__action-dropdown._active:after,.admin__action-dropdown.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.admin__action-dropdown:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;top:50%;transition:all .2s linear;width:0}._active .admin__action-dropdown:after,.active .admin__action-dropdown:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.admin__action-dropdown:hover:after{border-color:#000 transparent transparent}.admin__action-dropdown:focus,.admin__action-dropdown:hover{background-color:#fff;color:#000;text-decoration:none}.admin__action-dropdown:after{right:1.5rem}.admin__action-dropdown:before{margin-right:1rem}.admin__action-dropdown-menu{background-color:#fff;border:1px solid #007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5);display:none;line-height:1.36;margin-top:-1px;min-width:120%;padding:.5rem 1rem;position:absolute;top:100%;transition:all .15s ease;z-index:1}.admin__action-dropdown-menu>li{display:block}.admin__action-dropdown-menu>li>a{color:#333;display:block;text-decoration:none;padding:.6rem .5rem}.selectmenu{display:inline-block;position:relative;text-align:left;z-index:1}.selectmenu._active{border-color:#007bdb;z-index:500}.selectmenu .action-delete,.selectmenu .action-edit,.selectmenu .action-save{background-color:transparent;border-color:transparent;box-shadow:none;padding:0 1rem}.selectmenu .action-delete:hover,.selectmenu .action-edit:hover,.selectmenu .action-save:hover{background-color:transparent;border-color:transparent;box-shadow:none}.selectmenu .action-delete:before,.selectmenu .action-edit:before,.selectmenu .action-save:before{content:'\e630'}.selectmenu .action-delete,.selectmenu .action-edit{border:0 solid #fff;border-left-width:1px;bottom:0;position:absolute;right:0;top:0;z-index:1}.selectmenu .action-delete:hover,.selectmenu .action-edit:hover{border:0 solid #fff;border-left-width:1px}.selectmenu .action-save:before{content:'\e625'}.selectmenu .action-edit:before{content:'\e631'}.selectmenu-value{display:inline-block}.selectmenu-value input[type=text]{-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none;border:0;display:inline;margin:0;width:6rem}body._keyfocus .selectmenu-value input[type=text]:focus{box-shadow:none}.selectmenu-toggle{padding-right:3rem;background:0 0;border-width:0;bottom:0;float:right;position:absolute;right:0;top:0;width:0}.selectmenu-toggle._active:after,.selectmenu-toggle.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.selectmenu-toggle:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;right:1.1rem;top:50%;transition:all .2s linear;width:0}._active .selectmenu-toggle:after,.active .selectmenu-toggle:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.selectmenu-toggle:hover:after{border-color:#000 transparent transparent}.selectmenu-toggle:active,.selectmenu-toggle:focus,.selectmenu-toggle:hover{background:0 0}.selectmenu._active .selectmenu-toggle:before{border-color:#007bdb}body._keyfocus .selectmenu-toggle:focus{box-shadow:none}.selectmenu-toggle:before{background:#e3e3e3;border-left:1px solid #adadad;bottom:0;content:'';display:block;position:absolute;right:0;top:0;width:3.2rem}.selectmenu-items{background:#fff;border:1px solid #007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5);display:none;float:left;left:-1px;margin-top:3px;max-width:20rem;min-width:calc(100% + 2px);position:absolute;top:100%}.selectmenu-items._active{display:block}.selectmenu-items ul{float:left;list-style-type:none;margin:0;min-width:100%;padding:0}.selectmenu-items li{-webkit-flex-direction:row;display:flex;-ms-flex-direction:row;flex-direction:row;transition:background .2s linear}.selectmenu-items li:hover{background:#e3e3e3}.selectmenu-items li:last-child .selectmenu-item-action,.selectmenu-items li:last-child .selectmenu-item-action:visited{color:#008bdb;text-decoration:none}.selectmenu-items li:last-child .selectmenu-item-action:hover{color:#0fa7ff;text-decoration:underline}.selectmenu-items li:last-child .selectmenu-item-action:active{color:#ff5501;text-decoration:underline}.selectmenu-item{position:relative;width:100%;z-index:1}li._edit>.selectmenu-item{display:none}.selectmenu-item-edit{display:none;padding:.3rem 4rem .3rem .4rem;position:relative;white-space:nowrap;z-index:1}li:last-child .selectmenu-item-edit{padding-right:.4rem}.selectmenu-item-edit .admin__control-text{margin:0;width:5.4rem}li._edit .selectmenu-item-edit{display:block}.selectmenu-item-action{-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none;background:0 0;border:0;color:#333;display:block;font-size:1.4rem;font-weight:400;min-width:100%;padding:1rem 6rem 1rem 1.5rem;text-align:left;transition:background .2s linear;width:5rem}.selectmenu-item-action:focus,.selectmenu-item-action:hover{background:#e3e3e3}.abs-actions-split-xl .action-default,.page-actions .actions-split .action-default{margin-right:4rem}.abs-actions-split-xl .action-toggle,.page-actions .actions-split .action-toggle{padding-right:4rem}.abs-actions-split-xl .action-toggle:after,.page-actions .actions-split .action-toggle:after{border-width:.9rem .6rem 0;margin-top:-.3rem;right:1.4rem}.actions-split{position:relative;z-index:400}.actions-split._active,.actions-split.active,.actions-split:hover{box-shadow:0 0 0 1px #007bdb}.actions-split._active .action-toggle.action-primary,.actions-split._active .action-toggle.primary,.actions-split.active .action-toggle.action-primary,.actions-split.active .action-toggle.primary{background-color:#ba4000;border-color:#ba4000}.actions-split._active .dropdown-menu,.actions-split.active .dropdown-menu{opacity:1;visibility:visible;display:block}.actions-split .action-default,.actions-split .action-toggle{float:left;margin:0}.actions-split .action-default._active,.actions-split .action-default.active,.actions-split .action-default:hover,.actions-split .action-toggle._active,.actions-split .action-toggle.active,.actions-split .action-toggle:hover{box-shadow:none}.actions-split .action-default{margin-right:3.2rem;min-width:9.3rem}.actions-split .action-toggle{padding-right:3.2rem;border-left-color:rgba(0,0,0,.2);bottom:0;padding-left:0;position:absolute;right:0;top:0}.actions-split .action-toggle._active:after,.actions-split .action-toggle.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.actions-split .action-toggle:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;right:1.2rem;top:50%;transition:all .2s linear;width:0}._active .actions-split .action-toggle:after,.active .actions-split .action-toggle:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.actions-split .action-toggle:hover:after{border-color:#000 transparent transparent}.actions-split .action-toggle.action-primary:after,.actions-split .action-toggle.action-secondary:after,.actions-split .action-toggle.primary:after,.actions-split .action-toggle.secondary:after{border-color:#fff transparent transparent}.actions-split .action-toggle>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.action-select-wrap{display:inline-block;position:relative}.action-select-wrap .action-select{padding-right:3.2rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background-color:#fff;font-weight:400;text-align:left}.action-select-wrap .action-select._active:after,.action-select-wrap .action-select.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-select-wrap .action-select:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;right:1.2rem;top:50%;transition:all .2s linear;width:0}._active .action-select-wrap .action-select:after,.active .action-select-wrap .action-select:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-select-wrap .action-select:hover:after{border-color:#000 transparent transparent}.action-select-wrap .action-select:hover,.action-select-wrap .action-select:hover:before{border-color:#878787}.action-select-wrap .action-select:before{background-color:#e3e3e3;border:1px solid #adadad;bottom:0;content:'';position:absolute;right:0;top:0;width:3.2rem}.action-select-wrap .action-select._active{border-color:#007bdb}.action-select-wrap .action-select._active:before{border-color:#007bdb #007bdb #007bdb #adadad}.action-select-wrap .action-select[disabled]{color:#333}.action-select-wrap .action-select[disabled]:after{border-color:#333 transparent transparent}.action-select-wrap._active{z-index:500}.action-select-wrap._active .action-select,.action-select-wrap._active .action-select:before{border-color:#007bdb}.action-select-wrap._active .action-select:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-select-wrap .abs-action-menu .action-submenu,.action-select-wrap .abs-action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu,.action-select-wrap .action-menu .action-submenu,.action-select-wrap .actions-split .action-menu .action-submenu,.action-select-wrap .actions-split .action-menu .action-submenu .action-submenu,.action-select-wrap .actions-split .dropdown-menu .action-submenu,.action-select-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{max-height:45rem;overflow-y:auto}.action-select-wrap .abs-action-menu .action-submenu ._disabled:hover,.action-select-wrap .abs-action-menu .action-submenu .action-submenu ._disabled:hover,.action-select-wrap .action-menu ._disabled:hover,.action-select-wrap .action-menu .action-submenu ._disabled:hover,.action-select-wrap .actions-split .action-menu .action-submenu ._disabled:hover,.action-select-wrap .actions-split .action-menu .action-submenu .action-submenu ._disabled:hover,.action-select-wrap .actions-split .dropdown-menu .action-submenu ._disabled:hover,.action-select-wrap .actions-split .dropdown-menu .action-submenu .action-submenu ._disabled:hover{background:#fff}.action-select-wrap .abs-action-menu .action-submenu ._disabled .action-menu-item,.action-select-wrap .abs-action-menu .action-submenu .action-submenu ._disabled .action-menu-item,.action-select-wrap .action-menu ._disabled .action-menu-item,.action-select-wrap .action-menu .action-submenu ._disabled .action-menu-item,.action-select-wrap .actions-split .action-menu .action-submenu ._disabled .action-menu-item,.action-select-wrap .actions-split .action-menu .action-submenu .action-submenu ._disabled .action-menu-item,.action-select-wrap .actions-split .dropdown-menu .action-submenu ._disabled .action-menu-item,.action-select-wrap .actions-split .dropdown-menu .action-submenu .action-submenu ._disabled .action-menu-item{cursor:default;opacity:.5}.action-select-wrap .action-menu-items{left:0;position:absolute;right:0;top:100%}.action-select-wrap .action-menu-items>.abs-action-menu .action-submenu,.action-select-wrap .action-menu-items>.abs-action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.action-menu,.action-select-wrap .action-menu-items>.action-menu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .action-menu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .dropdown-menu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .dropdown-menu .action-submenu .action-submenu{min-width:100%;position:static}.action-select-wrap .action-menu-items>.abs-action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.abs-action-menu .action-submenu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.action-menu .action-submenu,.action-select-wrap .action-menu-items>.action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .action-menu .action-submenu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .dropdown-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .dropdown-menu .action-submenu .action-submenu .action-submenu{position:absolute}.action-multicheck-wrap{display:inline-block;height:1.6rem;padding-top:1px;position:relative;width:3.1rem;z-index:200}.action-multicheck-wrap:hover .action-multicheck-toggle,.action-multicheck-wrap:hover .admin__control-checkbox+label:before{border-color:#878787}.action-multicheck-wrap._active .action-multicheck-toggle,.action-multicheck-wrap._active .admin__control-checkbox+label:before{border-color:#007bdb}.action-multicheck-wrap._active .abs-action-menu .action-submenu,.action-multicheck-wrap._active .abs-action-menu .action-submenu .action-submenu,.action-multicheck-wrap._active .action-menu,.action-multicheck-wrap._active .action-menu .action-submenu,.action-multicheck-wrap._active .actions-split .action-menu .action-submenu,.action-multicheck-wrap._active .actions-split .action-menu .action-submenu .action-submenu,.action-multicheck-wrap._active .actions-split .dropdown-menu .action-submenu,.action-multicheck-wrap._active .actions-split .dropdown-menu .action-submenu .action-submenu{opacity:1;visibility:visible;display:block}.action-multicheck-wrap._disabled .admin__control-checkbox+label:before{background-color:#fff}.action-multicheck-wrap._disabled .action-multicheck-toggle,.action-multicheck-wrap._disabled .admin__control-checkbox+label:before{border-color:#adadad;opacity:1}.action-multicheck-wrap .action-multicheck-toggle,.action-multicheck-wrap .admin__control-checkbox,.action-multicheck-wrap .admin__control-checkbox+label{float:left}.action-multicheck-wrap .action-multicheck-toggle{border-radius:0 1px 1px 0;height:1.6rem;margin-left:-1px;padding:0;position:relative;transition:border-color .1s linear;width:1.6rem}.action-multicheck-wrap .action-multicheck-toggle._active:after,.action-multicheck-wrap .action-multicheck-toggle.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-multicheck-wrap .action-multicheck-toggle:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;top:50%;transition:all .2s linear;width:0}._active .action-multicheck-wrap .action-multicheck-toggle:after,.active .action-multicheck-wrap .action-multicheck-toggle:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-multicheck-wrap .action-multicheck-toggle:hover:after{border-color:#000 transparent transparent}.action-multicheck-wrap .action-multicheck-toggle:focus{border-color:#007bdb}.action-multicheck-wrap .action-multicheck-toggle:after{right:.3rem}.action-multicheck-wrap .abs-action-menu .action-submenu,.action-multicheck-wrap .abs-action-menu .action-submenu .action-submenu,.action-multicheck-wrap .action-menu,.action-multicheck-wrap .action-menu .action-submenu,.action-multicheck-wrap .actions-split .action-menu .action-submenu,.action-multicheck-wrap .actions-split .action-menu .action-submenu .action-submenu,.action-multicheck-wrap .actions-split .dropdown-menu .action-submenu,.action-multicheck-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{left:-1.1rem;margin-top:1px;right:auto;text-align:left}.action-multicheck-wrap .action-menu-item{white-space:nowrap}.admin__action-multiselect-wrap{display:block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.admin__action-multiselect-wrap.action-select-wrap:focus{box-shadow:none}.admin__action-multiselect-wrap.action-select-wrap .abs-action-menu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .abs-action-menu .action-submenu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .action-menu,.admin__action-multiselect-wrap.action-select-wrap .action-menu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .actions-split .action-menu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .actions-split .action-menu .action-submenu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .actions-split .dropdown-menu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{max-height:none;overflow-y:inherit}.admin__action-multiselect-wrap .action-menu-item{transition:background-color .1s linear}.admin__action-multiselect-wrap .action-menu-item._selected{background-color:#e0f6fe}.admin__action-multiselect-wrap .action-menu-item._hover{background-color:#e3e3e3}.admin__action-multiselect-wrap .action-menu-item._unclickable{cursor:default}.admin__action-multiselect-wrap .admin__action-multiselect{border:1px solid #adadad;cursor:pointer;display:block;min-height:3.2rem;padding-right:3.6rem;white-space:normal}.admin__action-multiselect-wrap .admin__action-multiselect:after{bottom:1.25rem;top:auto}.admin__action-multiselect-wrap .admin__action-multiselect:before{height:3.3rem;top:auto}.admin__control-table-wrapper .admin__action-multiselect-wrap{position:static}.admin__control-table-wrapper .admin__action-multiselect-wrap .admin__action-multiselect{position:relative}.admin__control-table-wrapper .admin__action-multiselect-wrap .admin__action-multiselect:before{right:-1px;top:-1px}.admin__control-table-wrapper .admin__action-multiselect-wrap .abs-action-menu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .abs-action-menu .action-submenu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .action-menu,.admin__control-table-wrapper .admin__action-multiselect-wrap .action-menu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .actions-split .action-menu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .actions-split .action-menu .action-submenu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .actions-split .dropdown-menu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{left:auto;min-width:34rem;right:auto;top:auto;z-index:1}.admin__action-multiselect-wrap .admin__action-multiselect-item-path{color:#a79d95;font-size:1.2rem;font-weight:400;padding-left:1rem}.admin__action-multiselect-actions-wrap{border-top:1px solid #e3e3e3;margin:0 1rem;padding:1rem 0;text-align:center}.admin__action-multiselect-actions-wrap .action-default{font-size:1.3rem;min-width:13rem}.admin__action-multiselect-text{padding:.6rem 1rem}.abs-action-menu .action-submenu,.abs-action-menu .action-submenu .action-submenu,.action-menu,.action-menu .action-submenu,.actions-split .action-menu .action-submenu,.actions-split .action-menu .action-submenu .action-submenu,.actions-split .dropdown-menu .action-submenu,.actions-split .dropdown-menu .action-submenu .action-submenu{text-align:left}.admin__action-multiselect-label{cursor:pointer;position:relative;z-index:1}.admin__action-multiselect-label:before{margin-right:.5rem}._unclickable .admin__action-multiselect-label{cursor:default;font-weight:700}.admin__action-multiselect-search-wrap{border-bottom:1px solid #e3e3e3;margin:0 1rem;padding:1rem 0;position:relative}.admin__action-multiselect-search{padding-right:3rem;width:100%}.admin__action-multiselect-search-label{display:block;font-size:1.5rem;height:1em;overflow:hidden;position:absolute;right:2.2rem;top:1.7rem;width:1em}.admin__action-multiselect-search-label:before{content:'\e60c'}.admin__action-multiselect-search-count{color:#a79d95;margin-top:1rem}.admin__action-multiselect-menu-inner{margin-bottom:0;max-height:46rem;overflow-y:auto}.admin__action-multiselect-menu-inner .admin__action-multiselect-menu-inner{list-style:none;max-height:none;overflow:hidden;padding-left:2.2rem}.admin__action-multiselect-menu-inner ._hidden{display:none}.admin__action-multiselect-crumb{background-color:#f5f5f5;border:1px solid #a79d95;border-radius:1px;display:inline-block;font-size:1.2rem;margin:.3rem -4px .3rem .3rem;padding:.3rem 2.4rem .4rem 1rem;position:relative;transition:border-color .1s linear}.admin__action-multiselect-crumb:hover{border-color:#908379}.admin__action-multiselect-crumb .action-close{bottom:0;font-size:.5em;position:absolute;right:0;top:0;width:2rem}.admin__action-multiselect-crumb .action-close:hover{color:#000}.admin__action-multiselect-crumb .action-close:active,.admin__action-multiselect-crumb .action-close:focus{background-color:transparent}.admin__action-multiselect-crumb .action-close:active{-ms-transform:scale(0.9);transform:scale(0.9)}.admin__action-multiselect-tree .abs-action-menu .action-submenu,.admin__action-multiselect-tree .abs-action-menu .action-submenu .action-submenu,.admin__action-multiselect-tree .action-menu,.admin__action-multiselect-tree .action-menu .action-submenu,.admin__action-multiselect-tree .actions-split .action-menu .action-submenu,.admin__action-multiselect-tree .actions-split .action-menu .action-submenu .action-submenu,.admin__action-multiselect-tree .actions-split .dropdown-menu .action-submenu,.admin__action-multiselect-tree .actions-split .dropdown-menu .action-submenu .action-submenu{min-width:34.7rem}.admin__action-multiselect-tree .abs-action-menu .action-submenu .action-menu-item,.admin__action-multiselect-tree .abs-action-menu .action-submenu .action-submenu .action-menu-item,.admin__action-multiselect-tree .action-menu .action-menu-item,.admin__action-multiselect-tree .action-menu .action-submenu .action-menu-item,.admin__action-multiselect-tree .actions-split .action-menu .action-submenu .action-menu-item,.admin__action-multiselect-tree .actions-split .action-menu .action-submenu .action-submenu .action-menu-item,.admin__action-multiselect-tree .actions-split .dropdown-menu .action-submenu .action-menu-item,.admin__action-multiselect-tree .actions-split .dropdown-menu .action-submenu .action-submenu .action-menu-item{margin-top:.1rem}.admin__action-multiselect-tree .action-menu-item{margin-left:4.2rem;position:relative}.admin__action-multiselect-tree .action-menu-item._expended:before{border-left:1px dashed #a79d95;bottom:0;content:'';left:-1rem;position:absolute;top:1rem;width:1px}.admin__action-multiselect-tree .action-menu-item._expended .admin__action-multiselect-dropdown:before{content:'\e615'}.admin__action-multiselect-tree .action-menu-item._with-checkbox .admin__action-multiselect-label{padding-left:2.6rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner{position:relative}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner .admin__action-multiselect-menu-inner{padding-left:3.2rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner .admin__action-multiselect-menu-inner:before{left:4.3rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item{position:relative}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:last-child:before{height:2.1rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:after,.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:before{content:'';left:0;position:absolute}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:after{border-top:1px dashed #a79d95;height:1px;top:2.1rem;width:5.2rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:before{border-left:1px dashed #a79d95;height:100%;top:0;width:1px}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._parent:after{width:4.2rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root{margin-left:-1rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root:after{left:3.2rem;width:2.2rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root:before{left:3.2rem;top:1rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root._parent:after{display:none}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root:first-child:before{top:2.1rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root:last-child:before{height:1rem}.admin__action-multiselect-tree .admin__action-multiselect-label{line-height:2.2rem;vertical-align:middle;word-break:break-all}.admin__action-multiselect-tree .admin__action-multiselect-label:before{left:0;position:absolute;top:.4rem}.admin__action-multiselect-dropdown{border-radius:50%;height:2.2rem;left:-2.2rem;position:absolute;top:1rem;width:2.2rem;z-index:1}.admin__action-multiselect-dropdown:before{background:#fff;color:#a79d95;content:'\e616';font-size:2.2rem}.admin__actions-switch{display:inline-block;position:relative;vertical-align:middle}.admin__field-control .admin__actions-switch{line-height:3.2rem}.admin__actions-switch+.admin__field-service{min-width:34rem}._disabled .admin__actions-switch-checkbox+.admin__actions-switch-label,.admin__actions-switch-checkbox.disabled+.admin__actions-switch-label{cursor:not-allowed;opacity:.5;pointer-events:none}.admin__actions-switch-checkbox:checked+.admin__actions-switch-label:before{left:15px}.admin__actions-switch-checkbox:checked+.admin__actions-switch-label:after{background:#79a22e}.admin__actions-switch-checkbox:checked+.admin__actions-switch-label .admin__actions-switch-text:before{content:attr(data-text-on)}.admin__actions-switch-checkbox:focus+.admin__actions-switch-label:after,.admin__actions-switch-checkbox:focus+.admin__actions-switch-label:before{border-color:#007bdb}._error .admin__actions-switch-checkbox+.admin__actions-switch-label:after,._error .admin__actions-switch-checkbox+.admin__actions-switch-label:before{border-color:#e22626}.admin__actions-switch-label{cursor:pointer;display:inline-block;height:22px;line-height:22px;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle}.admin__actions-switch-label:after,.admin__actions-switch-label:before{left:0;position:absolute;right:auto;top:0}.admin__actions-switch-label:before{background:#fff;border:1px solid #aaa6a0;border-radius:100%;content:'';display:block;height:22px;transition:left .2s ease-in 0s;width:22px;z-index:1}.admin__actions-switch-label:after{background:#e3e3e3;border:1px solid #aaa6a0;border-radius:12px;content:'';display:block;height:22px;transition:background .2s ease-in 0s;vertical-align:middle;width:37px;z-index:0}.admin__actions-switch-text:before{content:attr(data-text-off);padding-left:47px;white-space:nowrap}.abs-action-delete,.abs-action-reset,.action-close,.admin__field-fallback-reset,.extensions-information .list .extension-delete,.notifications-close,.search-global-field._active .search-global-action{background-color:transparent;border:none;border-radius:0;box-shadow:none;margin:0;padding:0}.abs-action-delete:hover,.abs-action-reset:hover,.action-close:hover,.admin__field-fallback-reset:hover,.extensions-information .list .extension-delete:hover,.notifications-close:hover,.search-global-field._active .search-global-action:hover{background-color:transparent;border:none;box-shadow:none}.abs-action-default,.abs-action-pattern,.abs-action-primary,.abs-action-quaternary,.abs-action-secondary,.abs-action-tertiary,.action-default,.action-primary,.action-quaternary,.action-secondary,.action-tertiary,.modal-popup .modal-footer .action-primary,.modal-popup .modal-footer .action-secondary,.page-actions .page-actions-buttons>button,.page-actions .page-actions-buttons>button.action-primary,.page-actions .page-actions-buttons>button.action-secondary,.page-actions .page-actions-buttons>button.primary,.page-actions>button,.page-actions>button.action-primary,.page-actions>button.action-secondary,.page-actions>button.primary,button,button.primary,button.secondary,button.tertiary{border:1px solid;border-radius:0;display:inline-block;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.4rem;font-weight:600;line-height:1.36;padding:.6rem 1em;text-align:center;vertical-align:baseline}.abs-action-default.disabled,.abs-action-default[disabled],.abs-action-pattern.disabled,.abs-action-pattern[disabled],.abs-action-primary.disabled,.abs-action-primary[disabled],.abs-action-quaternary.disabled,.abs-action-quaternary[disabled],.abs-action-secondary.disabled,.abs-action-secondary[disabled],.abs-action-tertiary.disabled,.abs-action-tertiary[disabled],.action-default.disabled,.action-default[disabled],.action-primary.disabled,.action-primary[disabled],.action-quaternary.disabled,.action-quaternary[disabled],.action-secondary.disabled,.action-secondary[disabled],.action-tertiary.disabled,.action-tertiary[disabled],.modal-popup .modal-footer .action-primary.disabled,.modal-popup .modal-footer .action-primary[disabled],.modal-popup .modal-footer .action-secondary.disabled,.modal-popup .modal-footer .action-secondary[disabled],.page-actions .page-actions-buttons>button.action-primary.disabled,.page-actions .page-actions-buttons>button.action-primary[disabled],.page-actions .page-actions-buttons>button.action-secondary.disabled,.page-actions .page-actions-buttons>button.action-secondary[disabled],.page-actions .page-actions-buttons>button.disabled,.page-actions .page-actions-buttons>button.primary.disabled,.page-actions .page-actions-buttons>button.primary[disabled],.page-actions .page-actions-buttons>button[disabled],.page-actions>button.action-primary.disabled,.page-actions>button.action-primary[disabled],.page-actions>button.action-secondary.disabled,.page-actions>button.action-secondary[disabled],.page-actions>button.disabled,.page-actions>button.primary.disabled,.page-actions>button.primary[disabled],.page-actions>button[disabled],button.disabled,button.primary.disabled,button.primary[disabled],button.secondary.disabled,button.secondary[disabled],button.tertiary.disabled,button.tertiary[disabled],button[disabled]{cursor:default;opacity:.5;pointer-events:none}.abs-action-l,.modal-popup .modal-footer .action-primary,.modal-popup .modal-footer .action-secondary,.page-actions .page-actions-buttons>button,.page-actions .page-actions-buttons>button.action-primary,.page-actions .page-actions-buttons>button.action-secondary,.page-actions .page-actions-buttons>button.primary,.page-actions button,.page-actions>button.action-primary,.page-actions>button.action-secondary,.page-actions>button.primary{font-size:1.6rem;letter-spacing:.025em;padding-bottom:.6875em;padding-top:.6875em}.abs-action-delete,.extensions-information .list .extension-delete{display:inline-block;font-size:1.6rem;margin-left:1.2rem;padding-top:.7rem;text-decoration:none;vertical-align:middle}.abs-action-delete:after,.extensions-information .list .extension-delete:after{color:#666;content:'\e630'}.abs-action-delete:hover:after,.extensions-information .list .extension-delete:hover:after{color:#35302c}.abs-action-button-as-link,.action-advanced,.data-grid .action-delete{line-height:1.36;padding:0;color:#008bdb;text-decoration:none;background:0 0;border:0;display:inline;font-weight:400;border-radius:0}.abs-action-button-as-link:visited,.action-advanced:visited,.data-grid .action-delete:visited{color:#008bdb;text-decoration:none}.abs-action-button-as-link:hover,.action-advanced:hover,.data-grid .action-delete:hover{text-decoration:underline}.abs-action-button-as-link:active,.action-advanced:active,.data-grid .action-delete:active{color:#ff5501;text-decoration:underline}.abs-action-button-as-link:hover,.action-advanced:hover,.data-grid .action-delete:hover{color:#0fa7ff}.abs-action-button-as-link:active,.abs-action-button-as-link:focus,.abs-action-button-as-link:hover,.action-advanced:active,.action-advanced:focus,.action-advanced:hover,.data-grid .action-delete:active,.data-grid .action-delete:focus,.data-grid .action-delete:hover{background:0 0;border:0}.abs-action-button-as-link.disabled,.abs-action-button-as-link[disabled],.action-advanced.disabled,.action-advanced[disabled],.data-grid .action-delete.disabled,.data-grid .action-delete[disabled],fieldset[disabled] .abs-action-button-as-link,fieldset[disabled] .action-advanced,fieldset[disabled] .data-grid .action-delete{color:#008bdb;opacity:.5;cursor:default;pointer-events:none;text-decoration:underline}.abs-action-button-as-link:active,.abs-action-button-as-link:not(:focus),.action-advanced:active,.action-advanced:not(:focus),.data-grid .action-delete:active,.data-grid .action-delete:not(:focus){box-shadow:none}.abs-action-button-as-link:focus,.action-advanced:focus,.data-grid .action-delete:focus{color:#0fa7ff}.abs-action-default,button{background:#e3e3e3;border-color:#adadad;color:#514943}.abs-action-default:active,.abs-action-default:focus,.abs-action-default:hover,button:active,button:focus,button:hover{background-color:#dbdbdb;color:#514943;text-decoration:none}.abs-action-primary,.page-actions .page-actions-buttons>button.action-primary,.page-actions .page-actions-buttons>button.primary,.page-actions>button.action-primary,.page-actions>button.primary,button.primary{background-color:#eb5202;border-color:#eb5202;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.25)}.abs-action-primary:active,.abs-action-primary:focus,.abs-action-primary:hover,.page-actions .page-actions-buttons>button.action-primary:active,.page-actions .page-actions-buttons>button.action-primary:focus,.page-actions .page-actions-buttons>button.action-primary:hover,.page-actions .page-actions-buttons>button.primary:active,.page-actions .page-actions-buttons>button.primary:focus,.page-actions .page-actions-buttons>button.primary:hover,.page-actions>button.action-primary:active,.page-actions>button.action-primary:focus,.page-actions>button.action-primary:hover,.page-actions>button.primary:active,.page-actions>button.primary:focus,.page-actions>button.primary:hover,button.primary:active,button.primary:focus,button.primary:hover{background-color:#ba4000;border-color:#b84002;box-shadow:0 0 0 1px #007bdb;color:#fff;text-decoration:none}.abs-action-primary.disabled,.abs-action-primary[disabled],.page-actions .page-actions-buttons>button.action-primary.disabled,.page-actions .page-actions-buttons>button.action-primary[disabled],.page-actions .page-actions-buttons>button.primary.disabled,.page-actions .page-actions-buttons>button.primary[disabled],.page-actions>button.action-primary.disabled,.page-actions>button.action-primary[disabled],.page-actions>button.primary.disabled,.page-actions>button.primary[disabled],button.primary.disabled,button.primary[disabled]{cursor:default;opacity:.5;pointer-events:none}.abs-action-secondary,.modal-popup .modal-footer .action-primary,.page-actions .page-actions-buttons>button.action-secondary,.page-actions>button.action-secondary,button.secondary{background-color:#514943;border-color:#514943;color:#fff;text-shadow:1px 1px 1px rgba(0,0,0,.3)}.abs-action-secondary:active,.abs-action-secondary:focus,.abs-action-secondary:hover,.modal-popup .modal-footer .action-primary:active,.modal-popup .modal-footer .action-primary:focus,.modal-popup .modal-footer .action-primary:hover,.page-actions .page-actions-buttons>button.action-secondary:active,.page-actions .page-actions-buttons>button.action-secondary:focus,.page-actions .page-actions-buttons>button.action-secondary:hover,.page-actions>button.action-secondary:active,.page-actions>button.action-secondary:focus,.page-actions>button.action-secondary:hover,button.secondary:active,button.secondary:focus,button.secondary:hover{background-color:#35302c;border-color:#35302c;box-shadow:0 0 0 1px #007bdb;color:#fff;text-decoration:none}.abs-action-secondary:active,.modal-popup .modal-footer .action-primary:active,.page-actions .page-actions-buttons>button.action-secondary:active,.page-actions>button.action-secondary:active,button.secondary:active{background-color:#35302c}.abs-action-tertiary,.modal-popup .modal-footer .action-secondary,button.tertiary{background-color:transparent;border-color:transparent;text-shadow:none;color:#008bdb}.abs-action-tertiary:active,.abs-action-tertiary:focus,.abs-action-tertiary:hover,.modal-popup .modal-footer .action-secondary:active,.modal-popup .modal-footer .action-secondary:focus,.modal-popup .modal-footer .action-secondary:hover,button.tertiary:active,button.tertiary:focus,button.tertiary:hover{background-color:transparent;border-color:transparent;box-shadow:none;color:#0fa7ff;text-decoration:underline}.abs-action-quaternary,.page-actions .page-actions-buttons>button,.page-actions>button{background-color:transparent;border-color:transparent;text-shadow:none;color:#333}.abs-action-quaternary:active,.abs-action-quaternary:focus,.abs-action-quaternary:hover,.page-actions .page-actions-buttons>button:active,.page-actions .page-actions-buttons>button:focus,.page-actions .page-actions-buttons>button:hover,.page-actions>button:active,.page-actions>button:focus,.page-actions>button:hover{background-color:transparent;border-color:transparent;box-shadow:none;color:#1a1a1a}.abs-action-menu,.actions-split .abs-action-menu .action-submenu,.actions-split .abs-action-menu .action-submenu .action-submenu,.actions-split .action-menu,.actions-split .action-menu .action-submenu,.actions-split .actions-split .dropdown-menu .action-submenu,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu,.actions-split .dropdown-menu{text-align:left;background-color:#fff;border:1px solid #007bdb;border-radius:1px;box-shadow:1px 1px 5px rgba(0,0,0,.5);color:#333;display:none;font-weight:400;left:0;list-style:none;margin:2px 0 0;min-width:0;padding:0;position:absolute;right:0;top:100%}.abs-action-menu._active,.actions-split .abs-action-menu .action-submenu .action-submenu._active,.actions-split .abs-action-menu .action-submenu._active,.actions-split .action-menu .action-submenu._active,.actions-split .action-menu._active,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu._active,.actions-split .actions-split .dropdown-menu .action-submenu._active,.actions-split .dropdown-menu._active{display:block}.abs-action-menu>li,.actions-split .abs-action-menu .action-submenu .action-submenu>li,.actions-split .abs-action-menu .action-submenu>li,.actions-split .action-menu .action-submenu>li,.actions-split .action-menu>li,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li,.actions-split .actions-split .dropdown-menu .action-submenu>li,.actions-split .dropdown-menu>li{border:none;display:block;padding:0;transition:background-color .1s linear}.abs-action-menu>li>a:hover,.actions-split .abs-action-menu .action-submenu .action-submenu>li>a:hover,.actions-split .abs-action-menu .action-submenu>li>a:hover,.actions-split .action-menu .action-submenu>li>a:hover,.actions-split .action-menu>li>a:hover,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li>a:hover,.actions-split .actions-split .dropdown-menu .action-submenu>li>a:hover,.actions-split .dropdown-menu>li>a:hover{text-decoration:none}.abs-action-menu>li._visible,.abs-action-menu>li:hover,.actions-split .abs-action-menu .action-submenu .action-submenu>li._visible,.actions-split .abs-action-menu .action-submenu .action-submenu>li:hover,.actions-split .abs-action-menu .action-submenu>li._visible,.actions-split .abs-action-menu .action-submenu>li:hover,.actions-split .action-menu .action-submenu>li._visible,.actions-split .action-menu .action-submenu>li:hover,.actions-split .action-menu>li._visible,.actions-split .action-menu>li:hover,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li._visible,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li:hover,.actions-split .actions-split .dropdown-menu .action-submenu>li._visible,.actions-split .actions-split .dropdown-menu .action-submenu>li:hover,.actions-split .dropdown-menu>li._visible,.actions-split .dropdown-menu>li:hover{background-color:#e3e3e3}.abs-action-menu>li:active,.actions-split .abs-action-menu .action-submenu .action-submenu>li:active,.actions-split .abs-action-menu .action-submenu>li:active,.actions-split .action-menu .action-submenu>li:active,.actions-split .action-menu>li:active,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li:active,.actions-split .actions-split .dropdown-menu .action-submenu>li:active,.actions-split .dropdown-menu>li:active{background-color:#cacaca}.abs-action-menu>li._parent,.actions-split .abs-action-menu .action-submenu .action-submenu>li._parent,.actions-split .abs-action-menu .action-submenu>li._parent,.actions-split .action-menu .action-submenu>li._parent,.actions-split .action-menu>li._parent,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li._parent,.actions-split .actions-split .dropdown-menu .action-submenu>li._parent,.actions-split .dropdown-menu>li._parent{-webkit-flex-direction:row;display:flex;-ms-flex-direction:row;flex-direction:row}.abs-action-menu>li._parent>.action-menu-item,.actions-split .abs-action-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.actions-split .abs-action-menu .action-submenu>li._parent>.action-menu-item,.actions-split .action-menu .action-submenu>li._parent>.action-menu-item,.actions-split .action-menu>li._parent>.action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu>li._parent>.action-menu-item,.actions-split .dropdown-menu>li._parent>.action-menu-item{min-width:100%}.abs-action-menu .action-menu-item,.abs-action-menu .item,.actions-split .abs-action-menu .action-submenu .action-menu-item,.actions-split .abs-action-menu .action-submenu .action-submenu .action-menu-item,.actions-split .abs-action-menu .action-submenu .action-submenu .item,.actions-split .abs-action-menu .action-submenu .item,.actions-split .action-menu .action-menu-item,.actions-split .action-menu .action-submenu .action-menu-item,.actions-split .action-menu .action-submenu .item,.actions-split .action-menu .item,.actions-split .actions-split .dropdown-menu .action-submenu .action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu .action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu .item,.actions-split .actions-split .dropdown-menu .action-submenu .item,.actions-split .dropdown-menu .action-menu-item,.actions-split .dropdown-menu .item{cursor:pointer;display:block;padding:.6875em 1em}.abs-action-menu .action-submenu,.actions-split .action-menu .action-submenu,.actions-split .action-menu .action-submenu .action-submenu,.actions-split .dropdown-menu .action-submenu{bottom:auto;left:auto;margin-left:0;margin-top:-1px;position:absolute;right:auto;top:auto}.ie9 .abs-action-menu .action-submenu,.ie9 .actions-split .abs-action-menu .action-submenu .action-submenu,.ie9 .actions-split .abs-action-menu .action-submenu .action-submenu .action-submenu,.ie9 .actions-split .action-menu .action-submenu,.ie9 .actions-split .action-menu .action-submenu .action-submenu,.ie9 .actions-split .actions-split .dropdown-menu .action-submenu .action-submenu,.ie9 .actions-split .actions-split .dropdown-menu .action-submenu .action-submenu .action-submenu,.ie9 .actions-split .dropdown-menu .action-submenu{margin-left:99%;margin-top:-3.5rem}.abs-action-menu a.action-menu-item,.actions-split .abs-action-menu .action-submenu .action-submenu a.action-menu-item,.actions-split .abs-action-menu .action-submenu a.action-menu-item,.actions-split .action-menu .action-submenu a.action-menu-item,.actions-split .action-menu a.action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu a.action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu a.action-menu-item,.actions-split .dropdown-menu a.action-menu-item{color:#333}.abs-action-menu a.action-menu-item:focus,.actions-split .abs-action-menu .action-submenu .action-submenu a.action-menu-item:focus,.actions-split .abs-action-menu .action-submenu a.action-menu-item:focus,.actions-split .action-menu .action-submenu a.action-menu-item:focus,.actions-split .action-menu a.action-menu-item:focus,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu a.action-menu-item:focus,.actions-split .actions-split .dropdown-menu .action-submenu a.action-menu-item:focus,.actions-split .dropdown-menu a.action-menu-item:focus{background-color:#e3e3e3;box-shadow:none}.abs-action-wrap-triangle{position:relative}.abs-action-wrap-triangle .action-default{width:100%}.abs-action-wrap-triangle .action-default:after,.abs-action-wrap-triangle .action-default:before{border-style:solid;content:'';height:0;position:absolute;top:0;width:0}.abs-action-wrap-triangle .action-default:active,.abs-action-wrap-triangle .action-default:focus,.abs-action-wrap-triangle .action-default:hover{box-shadow:none}._keyfocus .abs-action-wrap-triangle .action-default:focus{box-shadow:0 0 0 1px #007bdb}.ie10 .abs-action-wrap-triangle .action-default.disabled,.ie10 .abs-action-wrap-triangle .action-default[disabled],.ie9 .abs-action-wrap-triangle .action-default.disabled,.ie9 .abs-action-wrap-triangle .action-default[disabled]{background-color:#fcfcfc;opacity:1;text-shadow:none}.abs-action-wrap-triangle-right{display:inline-block;padding-right:1.6rem;position:relative}.abs-action-wrap-triangle-right .action-default:after,.abs-action-wrap-triangle-right .action-default:before{border-color:transparent transparent transparent #e3e3e3;border-width:1.7rem 0 1.6rem 1.7rem;left:100%;margin-left:-1.7rem}.abs-action-wrap-triangle-right .action-default:before{border-left-color:#949494;right:-1px}.abs-action-wrap-triangle-right .action-default:active:after,.abs-action-wrap-triangle-right .action-default:focus:after,.abs-action-wrap-triangle-right .action-default:hover:after{border-left-color:#dbdbdb}.ie10 .abs-action-wrap-triangle-right .action-default.disabled:after,.ie10 .abs-action-wrap-triangle-right .action-default[disabled]:after,.ie9 .abs-action-wrap-triangle-right .action-default.disabled:after,.ie9 .abs-action-wrap-triangle-right .action-default[disabled]:after{border-color:transparent transparent transparent #fcfcfc}.abs-action-wrap-triangle-right .action-primary:after{border-color:transparent transparent transparent #eb5202}.abs-action-wrap-triangle-right .action-primary:active:after,.abs-action-wrap-triangle-right .action-primary:focus:after,.abs-action-wrap-triangle-right .action-primary:hover:after{border-left-color:#ba4000}.abs-action-wrap-triangle-left{display:inline-block;padding-left:1.6rem}.abs-action-wrap-triangle-left .action-default{text-indent:-.85rem}.abs-action-wrap-triangle-left .action-default:after,.abs-action-wrap-triangle-left .action-default:before{border-color:transparent #e3e3e3 transparent transparent;border-width:1.7rem 1.7rem 1.6rem 0;margin-right:-1.7rem;right:100%}.abs-action-wrap-triangle-left .action-default:before{border-right-color:#949494;left:-1px}.abs-action-wrap-triangle-left .action-default:active:after,.abs-action-wrap-triangle-left .action-default:focus:after,.abs-action-wrap-triangle-left .action-default:hover:after{border-right-color:#dbdbdb}.ie10 .abs-action-wrap-triangle-left .action-default.disabled:after,.ie10 .abs-action-wrap-triangle-left .action-default[disabled]:after,.ie9 .abs-action-wrap-triangle-left .action-default.disabled:after,.ie9 .abs-action-wrap-triangle-left .action-default[disabled]:after{border-color:transparent #fcfcfc transparent transparent}.abs-action-wrap-triangle-left .action-primary:after{border-color:transparent #eb5202 transparent transparent}.abs-action-wrap-triangle-left .action-primary:active:after,.abs-action-wrap-triangle-left .action-primary:focus:after,.abs-action-wrap-triangle-left .action-primary:hover:after{border-right-color:#ba4000}.action-default,button{background:#e3e3e3;border-color:#adadad;color:#514943}.action-default:active,.action-default:focus,.action-default:hover,button:active,button:focus,button:hover{background-color:#dbdbdb;color:#514943;text-decoration:none}.action-primary{background-color:#eb5202;border-color:#eb5202;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.25)}.action-primary:active,.action-primary:focus,.action-primary:hover{background-color:#ba4000;border-color:#b84002;box-shadow:0 0 0 1px #007bdb;color:#fff;text-decoration:none}.action-primary.disabled,.action-primary[disabled]{cursor:default;opacity:.5;pointer-events:none}.action-secondary{background-color:#514943;border-color:#514943;color:#fff;text-shadow:1px 1px 1px rgba(0,0,0,.3)}.action-secondary:active,.action-secondary:focus,.action-secondary:hover{background-color:#35302c;border-color:#35302c;box-shadow:0 0 0 1px #007bdb;color:#fff;text-decoration:none}.action-secondary:active{background-color:#35302c}.action-quaternary,.action-tertiary{background-color:transparent;border-color:transparent;text-shadow:none}.action-quaternary:active,.action-quaternary:focus,.action-quaternary:hover,.action-tertiary:active,.action-tertiary:focus,.action-tertiary:hover{background-color:transparent;border-color:transparent;box-shadow:none}.action-tertiary{color:#008bdb}.action-tertiary:active,.action-tertiary:focus,.action-tertiary:hover{color:#0fa7ff;text-decoration:underline}.action-quaternary{color:#333}.action-quaternary:active,.action-quaternary:focus,.action-quaternary:hover{color:#1a1a1a}.action-close>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.action-close:active{-ms-transform:scale(0.9);transform:scale(0.9)}.action-close:before{content:'\e62f';transition:color .1s linear}.action-close:hover{cursor:pointer;text-decoration:none}.abs-action-menu .action-submenu,.abs-action-menu .action-submenu .action-submenu,.action-menu,.action-menu .action-submenu,.actions-split .action-menu .action-submenu,.actions-split .action-menu .action-submenu .action-submenu,.actions-split .dropdown-menu .action-submenu,.actions-split .dropdown-menu .action-submenu .action-submenu{background-color:#fff;border:1px solid #007bdb;border-radius:1px;box-shadow:1px 1px 5px rgba(0,0,0,.5);color:#333;display:none;font-weight:400;left:0;list-style:none;margin:2px 0 0;min-width:0;padding:0;position:absolute;right:0;top:100%}.abs-action-menu .action-submenu .action-submenu._active,.abs-action-menu .action-submenu._active,.action-menu .action-submenu._active,.action-menu._active,.actions-split .action-menu .action-submenu .action-submenu._active,.actions-split .action-menu .action-submenu._active,.actions-split .dropdown-menu .action-submenu .action-submenu._active,.actions-split .dropdown-menu .action-submenu._active{display:block}.abs-action-menu .action-submenu .action-submenu>li,.abs-action-menu .action-submenu>li,.action-menu .action-submenu>li,.action-menu>li,.actions-split .action-menu .action-submenu .action-submenu>li,.actions-split .action-menu .action-submenu>li,.actions-split .dropdown-menu .action-submenu .action-submenu>li,.actions-split .dropdown-menu .action-submenu>li{border:none;display:block;padding:0;transition:background-color .1s linear}.abs-action-menu .action-submenu .action-submenu>li>a:hover,.abs-action-menu .action-submenu>li>a:hover,.action-menu .action-submenu>li>a:hover,.action-menu>li>a:hover,.actions-split .action-menu .action-submenu .action-submenu>li>a:hover,.actions-split .action-menu .action-submenu>li>a:hover,.actions-split .dropdown-menu .action-submenu .action-submenu>li>a:hover,.actions-split .dropdown-menu .action-submenu>li>a:hover{text-decoration:none}.abs-action-menu .action-submenu .action-submenu>li._visible,.abs-action-menu .action-submenu .action-submenu>li:hover,.abs-action-menu .action-submenu>li._visible,.abs-action-menu .action-submenu>li:hover,.action-menu .action-submenu>li._visible,.action-menu .action-submenu>li:hover,.action-menu>li._visible,.action-menu>li:hover,.actions-split .action-menu .action-submenu .action-submenu>li._visible,.actions-split .action-menu .action-submenu .action-submenu>li:hover,.actions-split .action-menu .action-submenu>li._visible,.actions-split .action-menu .action-submenu>li:hover,.actions-split .dropdown-menu .action-submenu .action-submenu>li._visible,.actions-split .dropdown-menu .action-submenu .action-submenu>li:hover,.actions-split .dropdown-menu .action-submenu>li._visible,.actions-split .dropdown-menu .action-submenu>li:hover{background-color:#e3e3e3}.abs-action-menu .action-submenu .action-submenu>li:active,.abs-action-menu .action-submenu>li:active,.action-menu .action-submenu>li:active,.action-menu>li:active,.actions-split .action-menu .action-submenu .action-submenu>li:active,.actions-split .action-menu .action-submenu>li:active,.actions-split .dropdown-menu .action-submenu .action-submenu>li:active,.actions-split .dropdown-menu .action-submenu>li:active{background-color:#cacaca}.abs-action-menu .action-submenu .action-submenu>li._parent,.abs-action-menu .action-submenu>li._parent,.action-menu .action-submenu>li._parent,.action-menu>li._parent,.actions-split .action-menu .action-submenu .action-submenu>li._parent,.actions-split .action-menu .action-submenu>li._parent,.actions-split .dropdown-menu .action-submenu .action-submenu>li._parent,.actions-split .dropdown-menu .action-submenu>li._parent{-webkit-flex-direction:row;display:flex;-ms-flex-direction:row;flex-direction:row}.abs-action-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.abs-action-menu .action-submenu>li._parent>.action-menu-item,.action-menu .action-submenu>li._parent>.action-menu-item,.action-menu>li._parent>.action-menu-item,.actions-split .action-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.actions-split .action-menu .action-submenu>li._parent>.action-menu-item,.actions-split .dropdown-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.actions-split .dropdown-menu .action-submenu>li._parent>.action-menu-item{min-width:100%}.abs-action-menu .action-submenu .action-menu-item,.abs-action-menu .action-submenu .action-submenu .action-menu-item,.abs-action-menu .action-submenu .action-submenu .item,.abs-action-menu .action-submenu .item,.action-menu .action-menu-item,.action-menu .action-submenu .action-menu-item,.action-menu .action-submenu .item,.action-menu .item,.actions-split .action-menu .action-submenu .action-menu-item,.actions-split .action-menu .action-submenu .action-submenu .action-menu-item,.actions-split .action-menu .action-submenu .action-submenu .item,.actions-split .action-menu .action-submenu .item,.actions-split .dropdown-menu .action-submenu .action-menu-item,.actions-split .dropdown-menu .action-submenu .action-submenu .action-menu-item,.actions-split .dropdown-menu .action-submenu .action-submenu .item,.actions-split .dropdown-menu .action-submenu .item{cursor:pointer;display:block;padding:.6875em 1em}.abs-action-menu .action-submenu .action-submenu,.action-menu .action-submenu,.actions-split .action-menu .action-submenu .action-submenu,.actions-split .dropdown-menu .action-submenu .action-submenu{bottom:auto;left:auto;margin-left:0;margin-top:-1px;position:absolute;right:auto;top:auto}.ie9 .abs-action-menu .action-submenu .action-submenu,.ie9 .abs-action-menu .action-submenu .action-submenu .action-submenu,.ie9 .action-menu .action-submenu,.ie9 .action-menu .action-submenu .action-submenu,.ie9 .actions-split .action-menu .action-submenu .action-submenu,.ie9 .actions-split .action-menu .action-submenu .action-submenu .action-submenu,.ie9 .actions-split .dropdown-menu .action-submenu .action-submenu,.ie9 .actions-split .dropdown-menu .action-submenu .action-submenu .action-submenu{margin-left:99%;margin-top:-3.5rem}.abs-action-menu .action-submenu .action-submenu a.action-menu-item,.abs-action-menu .action-submenu a.action-menu-item,.action-menu .action-submenu a.action-menu-item,.action-menu a.action-menu-item,.actions-split .action-menu .action-submenu .action-submenu a.action-menu-item,.actions-split .action-menu .action-submenu a.action-menu-item,.actions-split .dropdown-menu .action-submenu .action-submenu a.action-menu-item,.actions-split .dropdown-menu .action-submenu a.action-menu-item{color:#333}.abs-action-menu .action-submenu .action-submenu a.action-menu-item:focus,.abs-action-menu .action-submenu a.action-menu-item:focus,.action-menu .action-submenu a.action-menu-item:focus,.action-menu a.action-menu-item:focus,.actions-split .action-menu .action-submenu .action-submenu a.action-menu-item:focus,.actions-split .action-menu .action-submenu a.action-menu-item:focus,.actions-split .dropdown-menu .action-submenu .action-submenu a.action-menu-item:focus,.actions-split .dropdown-menu .action-submenu a.action-menu-item:focus{background-color:#e3e3e3;box-shadow:none}.messages .message:last-child{margin:0 0 2rem}.message{background:#fffbbb;border:none;border-radius:0;color:#333;font-size:1.4rem;margin:0 0 1px;padding:1.8rem 4rem 1.8rem 5.5rem;position:relative;text-shadow:none}.message:before{background:0 0;border:0;color:#007bdb;content:'\e61a';font-family:Icons;font-size:1.9rem;font-style:normal;font-weight:400;height:auto;left:1.9rem;line-height:inherit;margin-top:-1.3rem;position:absolute;speak:none;text-shadow:none;top:50%;width:auto}.message-notice:before{color:#007bdb;content:'\e61a'}.message-warning:before{color:#eb5202;content:'\e623'}.message-error{background:#fcc}.message-error:before{color:#e22626;content:'\e632';font-size:1.5rem;left:2.2rem;margin-top:-1rem}.message-success:before{color:#79a22e;content:'\e62d'}.message-spinner:before{display:none}.message-spinner .spinner{font-size:2.5rem;left:1.5rem;position:absolute;top:1.5rem}.message-in-rating-edit{margin-left:1.8rem;margin-right:1.8rem}.modal-popup .action-close,.modal-slide .action-close{color:#736963;position:absolute;right:0;top:0;z-index:1}.modal-popup .action-close:active,.modal-slide .action-close:active{-ms-transform:none;transform:none}.modal-popup .action-close:active:before,.modal-slide .action-close:active:before{font-size:1.8rem}.modal-popup .action-close:hover:before,.modal-slide .action-close:hover:before{color:#58504b}.modal-popup .action-close:before,.modal-slide .action-close:before{font-size:2rem}.modal-popup .action-close:focus,.modal-slide .action-close:focus{background-color:transparent}.modal-popup.prompt .prompt-message{padding:2rem 0}.modal-popup.prompt .prompt-message input{width:100%}.modal-popup.confirm .modal-inner-wrap .message,.modal-popup.prompt .modal-inner-wrap .message{background:#fff}.modal-popup.modal-system-messages .modal-inner-wrap{background:#fffbbb}.modal-popup._image-box .modal-inner-wrap{margin:5rem auto;max-width:78rem;position:static}.modal-popup._image-box .thumbnail-preview{padding-bottom:3rem;text-align:center}.modal-popup._image-box .thumbnail-preview .thumbnail-preview-image-block{border:1px solid #ccc;margin:0 auto 2rem;max-width:58rem;padding:2rem}.modal-popup._image-box .thumbnail-preview .thumbnail-preview-image{max-height:54rem}.modal-popup .modal-title{font-size:2.4rem;margin-right:6.4rem}.modal-popup .modal-footer{padding-top:2.6rem;text-align:right}.modal-popup .action-close{padding:3rem}.modal-popup .action-close:active,.modal-popup .action-close:focus{background:0 0;padding-right:3.1rem;padding-top:3.1rem}.modal-slide .modal-content-new-attribute{-webkit-overflow-scrolling:touch;overflow:auto;padding-bottom:0}.modal-slide .modal-content-new-attribute iframe{margin-bottom:-2.5rem}.modal-slide .modal-title{font-size:2.1rem;margin-right:5.7rem}.modal-slide .action-close{padding:2.1rem 2.6rem}.modal-slide .action-close:active{padding-right:2.7rem;padding-top:2.2rem}.modal-slide .page-main-actions{margin-bottom:.6rem;margin-top:2.1rem}.modal-slide .magento-message{padding:0 3rem 3rem;position:relative}.modal-slide .magento-message .insert-title-inner,.modal-slide .main-col .insert-title-inner{border-bottom:1px solid #adadad;margin:0 0 2rem;padding-bottom:.5rem}.modal-slide .magento-message .insert-actions,.modal-slide .main-col .insert-actions{float:right}.modal-slide .magento-message .title,.modal-slide .main-col .title{font-size:1.6rem;padding-top:.5rem}.modal-slide .main-col,.modal-slide .side-col{float:left;padding-bottom:0}.modal-slide .main-col:after,.modal-slide .side-col:after{display:none}.modal-slide .side-col{width:20%}.modal-slide .main-col{padding-right:0;width:80%}.modal-slide .content-footer .form-buttons{float:right}.modal-title{font-weight:400;margin-bottom:0;min-height:1em}.modal-title span{font-size:1.4rem;font-style:italic;margin-left:1rem}.spinner{display:inline-block;font-size:4rem;height:1em;margin-right:1.5rem;position:relative;width:1em}.spinner>span:nth-child(1){animation-delay:.27s;-ms-transform:rotate(-315deg);transform:rotate(-315deg)}.spinner>span:nth-child(2){animation-delay:.36s;-ms-transform:rotate(-270deg);transform:rotate(-270deg)}.spinner>span:nth-child(3){animation-delay:.45s;-ms-transform:rotate(-225deg);transform:rotate(-225deg)}.spinner>span:nth-child(4){animation-delay:.54s;-ms-transform:rotate(-180deg);transform:rotate(-180deg)}.spinner>span:nth-child(5){animation-delay:.63s;-ms-transform:rotate(-135deg);transform:rotate(-135deg)}.spinner>span:nth-child(6){animation-delay:.72s;-ms-transform:rotate(-90deg);transform:rotate(-90deg)}.spinner>span:nth-child(7){animation-delay:.81s;-ms-transform:rotate(-45deg);transform:rotate(-45deg)}.spinner>span:nth-child(8){animation-delay:.9;-ms-transform:rotate(0deg);transform:rotate(0deg)}@keyframes fade{0%{background-color:#514943}100%{background-color:#fff}}.spinner>span{-ms-transform:scale(0.4);transform:scale(0.4);animation-name:fade;animation-duration:.72s;animation-iteration-count:infinite;animation-direction:linear;background-color:#fff;border-radius:6px;clip:rect(0 .28571429em .1em 0);height:.1em;margin-top:.5em;position:absolute;width:1em}.ie9 .spinner{background:url(../images/ajax-loader.gif) center no-repeat}.ie9 .spinner>span{display:none}.popup-loading{background:rgba(255,255,255,.8);border-color:#ef672f;color:#ef672f;font-size:14px;font-weight:700;left:50%;margin-left:-100px;padding:100px 0 10px;position:fixed;text-align:center;top:40%;width:200px;z-index:1003}.popup-loading:after{background-image:url(../images/loader-1.gif);content:'';height:64px;left:50%;margin:-32px 0 0 -32px;position:absolute;top:40%;width:64px;z-index:2}.loading-mask,.loading-old{background:rgba(255,255,255,.4);bottom:0;left:0;position:fixed;right:0;top:0;z-index:2003}.loading-mask img,.loading-old img{display:none}.loading-mask p,.loading-old p{margin-top:118px}.loading-mask .loader,.loading-old .loader{background:url(../images/loader-1.gif) 50% 30% no-repeat #f7f3eb;border-radius:5px;bottom:0;color:#575757;font-size:14px;font-weight:700;height:160px;left:0;margin:auto;opacity:.95;position:absolute;right:0;text-align:center;top:0;width:160px}.admin-user{float:right;line-height:1.36;margin-left:.3rem;z-index:490}.admin-user._active .admin__action-dropdown,.admin-user.active .admin__action-dropdown{border-color:#007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5)}.admin-user .admin__action-dropdown{height:3.3rem;padding:.7rem 2.8rem .4rem 4rem}.admin-user .admin__action-dropdown._active:after,.admin-user .admin__action-dropdown.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.admin-user .admin__action-dropdown:after{border-color:#777 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;right:1.3rem;top:50%;transition:all .2s linear;width:0}._active .admin-user .admin__action-dropdown:after,.active .admin-user .admin__action-dropdown:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.admin-user .admin__action-dropdown:hover:after{border-color:#000 transparent transparent}.admin-user .admin__action-dropdown:before{color:#777;content:'\e600';font-size:2rem;left:1.1rem;margin-top:-1.1rem;position:absolute;top:50%}.admin-user .admin__action-dropdown:hover:before{color:#333}.admin-user .admin__action-dropdown-menu{min-width:20rem;padding-left:1rem;padding-right:1rem}.admin-user .admin__action-dropdown-menu>li>a{padding-left:.5em;padding-right:1.8rem;transition:background-color .1s linear;white-space:nowrap}.admin-user .admin__action-dropdown-menu>li>a:hover{background-color:#e0f6fe;color:#333}.admin-user .admin__action-dropdown-menu>li>a:active{background-color:#c7effd;bottom:-1px;position:relative}.admin-user .admin__action-dropdown-menu .admin-user-name{text-overflow:ellipsis;white-space:nowrap;display:inline-block;max-width:20rem;overflow:hidden;vertical-align:top}.admin-user-account-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;max-width:11.2rem}.search-global{float:right;margin-right:-.3rem;position:relative;z-index:480}.search-global-field{min-width:5rem}.search-global-field._active .search-global-input{background-color:#fff;border-color:#007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5);padding-right:4rem;width:25rem}.search-global-field._active .search-global-action{display:block;height:3.3rem;position:absolute;right:0;text-indent:-100%;top:0;width:5rem;z-index:3}.search-global-field .autocomplete-results{height:3.3rem;position:absolute;right:0;top:0;width:25rem}.search-global-field .search-global-menu{border:1px solid #007bdb;border-top-color:transparent;box-shadow:1px 1px 5px rgba(0,0,0,.5);left:0;margin-top:-2px;padding:0;position:absolute;right:0;top:100%;z-index:2}.search-global-field .search-global-menu:after{background-color:#fff;content:'';height:5px;left:0;position:absolute;right:0;top:-5px}.search-global-field .search-global-menu>li{background-color:#fff;border-top:1px solid #ddd;display:block;font-size:1.2rem;padding:.75rem 1.4rem .55rem}.search-global-field .search-global-menu>li._active{background-color:#e0f6fe}.search-global-field .search-global-menu .title{display:block;font-size:1.4rem}.search-global-field .search-global-menu .type{color:#1a1a1a;display:block}.search-global-label{cursor:pointer;height:3.3rem;padding:.75rem 1.4rem .55rem;position:absolute;right:0;top:0;z-index:2}.search-global-label:active{-ms-transform:scale(0.9);transform:scale(0.9)}.search-global-label:hover:before{color:#000}.search-global-label:before{color:#777;content:'\e60c';font-size:2rem}.search-global-input{background-color:transparent;border:1px solid transparent;font-size:1.4rem;height:3.3rem;padding:.75rem 1.4rem .55rem;position:absolute;right:0;top:0;transition:all .1s linear,width .3s linear;width:5rem;z-index:1}.search-global-action{display:none}.notifications-wrapper{float:right;line-height:1;position:relative}.notifications-wrapper.active{z-index:500}.notifications-wrapper.active .notifications-action{border-color:#007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5)}.notifications-wrapper.active .notifications-action:after{background-color:#fff;border:none;content:'';display:block;height:6px;left:-6px;margin-top:0;position:absolute;right:0;top:100%;width:auto}.notifications-wrapper .admin__action-dropdown-menu{padding:1rem 0 0;width:32rem}.notifications-action{color:#777;height:3.3rem;padding:.75rem 2rem .65rem}.notifications-action:after{display:none}.notifications-action:before{content:'\e607';font-size:1.9rem;margin-right:0}.notifications-action:active:before{position:relative;top:1px}.notifications-action .notifications-counter{background-color:#e22626;border-radius:1em;color:#fff;display:inline-block;font-size:1.1rem;font-weight:700;left:50%;margin-left:.3em;margin-top:-1.1em;padding:.3em .5em;position:absolute;top:50%}.notifications-entry{line-height:1.36;padding:.6rem 2rem .8rem;position:relative;transition:background-color .1s linear}.notifications-entry:hover{background-color:#e0f6fe}.notifications-entry.notifications-entry-last{margin:0 2rem;padding:.3rem 0 1.3rem;text-align:center}.notifications-entry.notifications-entry-last:hover{background-color:transparent}.notifications-entry+.notifications-entry-last{border-top:1px solid #ddd;padding-bottom:.6rem}.notifications-entry ._cutted{cursor:pointer}.notifications-entry ._cutted .notifications-entry-description-start:after{content:'...'}.notifications-entry-title{color:#ef672f;display:block;font-size:1.1rem;font-weight:700;margin-bottom:.7rem;margin-right:1em}.notifications-entry-description{color:#333;font-size:1.1rem;margin-bottom:.8rem}.notifications-entry-description-end{display:none}.notifications-entry-description-end._show{display:inline}.notifications-entry-time{color:#777;font-size:1.1rem}.notifications-close{line-height:1;padding:1rem;position:absolute;right:0;top:.6rem}.notifications-close:before{color:#ccc;content:'\e620';transition:color .1s linear}.notifications-close:hover:before{color:#b3b3b3}.notifications-close:active{-ms-transform:scale(0.95);transform:scale(0.95)}.page-header-actions{padding-top:1.1rem}.page-header-hgroup{padding-right:1.5rem}.page-title{color:#333;font-size:2.8rem}.page-header{padding:1.5rem 3rem}.menu-wrapper{display:inline-block;position:relative;width:8.8rem;z-index:700}.menu-wrapper:before{background-color:#373330;bottom:0;content:'';left:0;position:fixed;top:0;width:8.8rem;z-index:699}.menu-wrapper._fixed{left:0;position:fixed;top:0}.menu-wrapper._fixed~.page-wrapper{margin-left:8.8rem}.menu-wrapper .logo{display:block;height:8.8rem;padding:2.4rem 0 2.2rem;position:relative;text-align:center;z-index:700}._keyfocus .menu-wrapper .logo:focus{background-color:#4a4542;box-shadow:none}._keyfocus .menu-wrapper .logo:focus+.admin__menu .level-0:first-child>a{background-color:#373330}._keyfocus .menu-wrapper .logo:focus+.admin__menu .level-0:first-child>a:after{display:none}.menu-wrapper .logo:hover .logo-img{-webkit-filter:brightness(1.1);filter:brightness(1.1)}.menu-wrapper .logo:active .logo-img{-ms-transform:scale(0.95);transform:scale(0.95)}.menu-wrapper .logo .logo-img{height:4.2rem;transition:-webkit-filter .2s linear,filter .2s linear,transform .1s linear;width:3.5rem}.abs-menu-separator,.admin__menu .item-partners>a:after,.admin__menu .level-0:first-child>a:after{background-color:#736963;content:'';display:block;height:1px;left:0;margin-left:16%;position:absolute;top:0;width:68%}.admin__menu li{display:block}.admin__menu .level-0:first-child>a{position:relative}.admin__menu .level-0._active>a,.admin__menu .level-0:hover>a{color:#f7f3eb}.admin__menu .level-0._active>a{background-color:#524d49}.admin__menu .level-0:hover>a{background-color:#4a4542}.admin__menu .level-0>a{color:#aaa6a0;display:block;font-size:1rem;letter-spacing:.025em;min-height:6.2rem;padding:1.2rem .5rem .5rem;position:relative;text-align:center;text-decoration:none;text-transform:uppercase;transition:background-color .1s linear;word-wrap:break-word;z-index:700}.admin__menu .level-0>a:focus{box-shadow:none}.admin__menu .level-0>a:before{content:'\e63a';display:block;font-size:2.2rem;height:2.2rem}.admin__menu .level-0>.submenu{background-color:#4a4542;box-shadow:0 0 3px #000;left:100%;min-height:calc(8.8rem + 2rem + 100%);padding:2rem 0 0;position:absolute;top:0;-ms-transform:translateX(-100%);transform:translateX(-100%);transition-duration:.3s;transition-property:transform,visibility;transition-timing-function:ease-in-out;visibility:hidden;z-index:697}.ie10 .admin__menu .level-0>.submenu,.ie11 .admin__menu .level-0>.submenu{height:100%}.admin__menu .level-0._show>.submenu{-ms-transform:translateX(0);transform:translateX(0);visibility:visible;z-index:698}.admin__menu .level-1{margin-left:1.5rem;margin-right:1.5rem}.admin__menu [class*=level-]:not(.level-0) a{display:block;padding:1.25rem 1.5rem}.admin__menu [class*=level-]:not(.level-0) a:hover{background-color:#403934}.admin__menu [class*=level-]:not(.level-0) a:active{background-color:#322c29;padding-bottom:1.15rem;padding-top:1.35rem}.admin__menu .submenu li{min-width:23.8rem}.admin__menu .submenu a{color:#fcfcfc;transition:background-color .1s linear}.admin__menu .submenu a:focus,.admin__menu .submenu a:hover{box-shadow:none;text-decoration:none}._keyfocus .admin__menu .submenu a:focus{background-color:#403934}._keyfocus .admin__menu .submenu a:active{background-color:#322c29}.admin__menu .submenu .parent{margin-bottom:4.5rem}.admin__menu .submenu .parent .submenu-group-title{color:#a79d95;display:block;font-size:1.6rem;font-weight:600;margin-bottom:.7rem;padding:1.25rem 1.5rem;pointer-events:none}.admin__menu .submenu .column{display:table-cell}.admin__menu .submenu-title{color:#fff;display:block;font-size:2.2rem;font-weight:600;margin-bottom:4.2rem;margin-left:3rem;margin-right:5.8rem}.admin__menu .submenu-sub-title{color:#fff;display:block;font-size:1.2rem;margin:-3.8rem 5.8rem 3.8rem 3rem}.admin__menu .action-close{padding:2.4rem 2.8rem;position:absolute;right:0;top:0}.admin__menu .action-close:before{color:#a79d95;font-size:1.7rem}.admin__menu .action-close:hover:before{color:#fff}.admin__menu .item-dashboard>a:before{content:'\e604';font-size:1.8rem;padding-top:.4rem}.admin__menu .item-sales>a:before{content:'\e60b'}.admin__menu .item-catalog>a:before{content:'\e608'}.admin__menu .item-customer>a:before{content:'\e603';font-size:2.6rem;position:relative;top:-.4rem}.admin__menu .item-marketing>a:before{content:'\e609';font-size:2rem;padding-top:.2rem}.admin__menu .item-content>a:before{content:'\e602';font-size:2.4rem;position:relative;top:-.2rem}.admin__menu .item-report>a:before{content:'\e60a'}.admin__menu .item-stores>a:before{content:'\e60d';font-size:1.9rem;padding-top:.3rem}.admin__menu .item-system>a:before{content:'\e610'}.admin__menu .item-partners._active>a:after,.admin__menu .item-system._current+.item-partners>a:after{display:none}.admin__menu .item-partners>a{padding-bottom:1rem}.admin__menu .item-partners>a:before{content:'\e612'}.admin__menu .level-0>.submenu>ul>.level-1:only-of-type>.submenu-group-title,.admin__menu .submenu .column:only-of-type .submenu-group-title{display:none}.admin__menu-overlay{bottom:0;left:0;position:fixed;right:0;top:0;z-index:697}.store-switcher{color:#333;float:left;font-size:1.3rem;margin-top:.7rem}.store-switcher .admin__action-dropdown{background-color:#f8f8f8;margin-left:.5em}.store-switcher .dropdown{display:inline-block;position:relative}.store-switcher .dropdown:after,.store-switcher .dropdown:before{content:'';display:table}.store-switcher .dropdown:after{clear:both}.store-switcher .dropdown .action.toggle{cursor:pointer;display:inline-block;text-decoration:none}.store-switcher .dropdown .action.toggle:after{-webkit-font-smoothing:antialiased;font-size:22px;line-height:2;color:#333;content:'\e607';font-family:icons-blank-theme;margin:0;vertical-align:top;display:inline-block;font-weight:400;overflow:hidden;speak:none;text-align:center}.store-switcher .dropdown .action.toggle:active:after,.store-switcher .dropdown .action.toggle:hover:after{color:#333}.store-switcher .dropdown .action.toggle.active{display:inline-block;text-decoration:none}.store-switcher .dropdown .action.toggle.active:after{-webkit-font-smoothing:antialiased;font-size:22px;line-height:2;color:#333;content:'\e618';font-family:icons-blank-theme;margin:0;vertical-align:top;display:inline-block;font-weight:400;overflow:hidden;speak:none;text-align:center}.store-switcher .dropdown .action.toggle.active:active:after,.store-switcher .dropdown .action.toggle.active:hover:after{color:#333}.store-switcher .dropdown .dropdown-menu{margin:4px 0 0;padding:0;list-style:none;background:#fff;border:1px solid #aaa6a0;min-width:19.5rem;z-index:100;box-sizing:border-box;display:none;position:absolute;top:100%;box-shadow:1px 1px 5px rgba(0,0,0,.5)}.store-switcher .dropdown .dropdown-menu li{margin:0;padding:0}.store-switcher .dropdown .dropdown-menu li:hover{background:0 0;cursor:pointer}.store-switcher .dropdown.active{overflow:visible}.store-switcher .dropdown.active .dropdown-menu{display:block}.store-switcher .dropdown-menu{left:0;margin-top:.5em;max-height:250px;overflow-y:auto;padding-top:.25em}.store-switcher .dropdown-menu li{border:0;cursor:default}.store-switcher .dropdown-menu li:hover{cursor:default}.store-switcher .dropdown-menu li a,.store-switcher .dropdown-menu li span{color:#333;display:block;padding:.5rem 1.3rem}.store-switcher .dropdown-menu li a{text-decoration:none}.store-switcher .dropdown-menu li a:hover{background:#e9e9e9}.store-switcher .dropdown-menu li span{color:#adadad;cursor:default}.store-switcher .dropdown-menu li.current span{background:#eee;color:#333}.store-switcher .dropdown-menu .store-switcher-store a,.store-switcher .dropdown-menu .store-switcher-store span{padding-left:2.6rem}.store-switcher .dropdown-menu .store-switcher-store-view a,.store-switcher .dropdown-menu .store-switcher-store-view span{padding-left:3.9rem}.store-switcher .dropdown-menu .dropdown-toolbar{border-top:1px solid #ebebeb;margin-top:1rem}.store-switcher .dropdown-menu .dropdown-toolbar a:before{content:'\e610';margin-right:.25em;position:relative;top:1px}.store-switcher-label{font-weight:700}.store-switcher-alt{display:inline-block;position:relative}.store-switcher-alt.active .dropdown-menu{display:block}.store-switcher-alt .dropdown-menu{margin-top:2px;white-space:nowrap}.store-switcher-alt .dropdown-menu ul{list-style:none;margin:0;padding:0}.store-switcher-alt strong{color:#a79d95;display:block;font-size:14px;font-weight:500;line-height:1.333;padding:5px 10px}.store-switcher-alt .store-selected{color:#676056;cursor:pointer;font-size:12px;font-weight:400;line-height:1.333}.store-switcher-alt .store-selected:after{-webkit-font-smoothing:antialiased;color:#afadac;content:'\e02c';font-style:normal;font-weight:400;margin:0 0 0 3px;speak:none;vertical-align:text-top}.store-switcher-alt .store-switcher-store,.store-switcher-alt .store-switcher-website{padding:0}.store-switcher-alt .store-switcher-store:hover,.store-switcher-alt .store-switcher-website:hover{background:0 0}.store-switcher-alt .manage-stores,.store-switcher-alt .store-switcher-all,.store-switcher-alt .store-switcher-store-view{padding:0}.store-switcher-alt .manage-stores>a,.store-switcher-alt .store-switcher-all>a{color:#676056;display:block;font-size:12px;padding:8px 15px;text-decoration:none}.store-switcher-website{margin:5px 0 0}.store-switcher-website>strong{padding-left:13px}.store-switcher-store{margin:1px 0 0}.store-switcher-store>strong{padding-left:20px}.store-switcher-store>ul{margin-top:1px}.store-switcher-store-view:first-child{border-top:1px solid #e5e5e5}.store-switcher-store-view>a{color:#333;display:block;font-size:13px;padding:5px 15px 5px 24px;text-decoration:none}.store-view:not(.store-switcher){float:left}.store-view .store-switcher-label{display:inline-block;margin-top:1rem}.tooltip{margin-left:.5em}.tooltip .help a,.tooltip .help span{cursor:pointer;display:inline-block;height:22px;position:relative;vertical-align:middle;width:22px;z-index:2}.tooltip .help a:before,.tooltip .help span:before{color:#333;content:'\e633';font-size:1.7rem}.tooltip .help a:hover{text-decoration:none}.tooltip .tooltip-content{background:#000;border-radius:3px;color:#fff;display:none;margin-left:-19px;margin-top:10px;max-width:200px;padding:4px 8px;position:absolute;text-shadow:none;z-index:20}.tooltip .tooltip-content:before{border-bottom:5px solid #000;border-left:5px solid transparent;border-right:5px solid transparent;content:'';height:0;left:20px;opacity:.8;position:absolute;top:-5px;width:0}.tooltip .tooltip-content.loading{position:absolute}.tooltip .tooltip-content.loading:before{border-bottom-color:rgba(0,0,0,.3)}.tooltip:hover>.tooltip-content{display:block}.page-actions._fixed,.page-main-actions:not(._hidden){background:#f8f8f8;border-bottom:1px solid #e3e3e3;border-top:1px solid #e3e3e3;padding:1.5rem}.page-main-actions{margin:0 0 3rem}.page-main-actions._hidden .store-switcher{display:none}.page-main-actions._hidden .page-actions-placeholder{min-height:50px}.page-actions{float:right}.page-main-actions .page-actions._fixed{left:8.8rem;position:fixed;right:0;top:0;z-index:501}.page-main-actions .page-actions._fixed .page-actions-inner:before{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#333;content:attr(data-title);float:left;font-size:2.8rem;margin-top:.3rem;max-width:50%}.page-actions .page-actions-buttons>button,.page-actions>button{float:right;margin-left:1.3rem}.page-actions .page-actions-buttons>button.action-back,.page-actions .page-actions-buttons>button.back,.page-actions>button.action-back,.page-actions>button.back{float:left;-ms-flex-order:-1;order:-1}.page-actions .page-actions-buttons>button.action-back:before,.page-actions .page-actions-buttons>button.back:before,.page-actions>button.action-back:before,.page-actions>button.back:before{content:'\e626';margin-right:.5em;position:relative;top:1px}.page-actions .page-actions-buttons>button.action-primary,.page-actions .page-actions-buttons>button.primary,.page-actions>button.action-primary,.page-actions>button.primary{-ms-flex-order:2;order:2}.page-actions .page-actions-buttons>button.save:not(.primary),.page-actions>button.save:not(.primary){-ms-flex-order:1;order:1}.page-actions .page-actions-buttons>button.delete,.page-actions>button.delete{-ms-flex-order:-1;order:-1}.page-actions .actions-split{float:right;margin-left:1.3rem;-ms-flex-order:2;order:2}.page-actions .actions-split .dropdown-menu .item{display:block}.page-actions-buttons{float:right;-ms-flex-pack:end;justify-content:flex-end;display:-ms-flexbox;display:flex}.customer-index-edit .page-actions-buttons{background-color:transparent}.admin__page-nav{background:#f1f1f1;border:1px solid #e3e3e3}.admin__page-nav._collapsed:first-child{border-bottom:none}.admin__page-nav._collapsed._show{border-bottom:1px solid #e3e3e3}.admin__page-nav._collapsed._show ._collapsible{background:#f1f1f1}.admin__page-nav._collapsed._show ._collapsible:after{content:'\e62b'}.admin__page-nav._collapsed._show ._collapsible+.admin__page-nav-items{display:block}.admin__page-nav._collapsed._hide .admin__page-nav-title-messages,.admin__page-nav._collapsed._hide .admin__page-nav-title-messages ._active{display:inline-block}.admin__page-nav+._collapsed{border-bottom:none;border-top:none}.admin__page-nav-title{border-bottom:1px solid #e3e3e3;color:#303030;display:block;font-size:1.4rem;line-height:1.2;margin:0 0 -1px;padding:1.8rem 1.5rem;position:relative;text-transform:uppercase}.admin__page-nav-title._collapsible{background:#fff;cursor:pointer;margin:0;padding-right:3.5rem;transition:border-color .1s ease-out,background-color .1s ease-out}.admin__page-nav-title._collapsible+.admin__page-nav-items{display:none;margin-top:-1px}.admin__page-nav-title._collapsible:after{content:'\e628';font-size:1.3rem;font-weight:700;position:absolute;right:1.8rem;top:2rem}.admin__page-nav-title._collapsible:hover{background:#f1f1f1}.admin__page-nav-title._collapsible:last-child{margin:0 0 -1px}.admin__page-nav-title strong{font-weight:700}.admin__page-nav-title .admin__page-nav-title-messages{display:none}.admin__page-nav-items{list-style-type:none;margin:0;padding:1rem 0 1.3rem}.admin__page-nav-item{border-left:3px solid transparent;margin-left:.7rem;padding:0;position:relative;transition:border-color .1s ease-out,background-color .1s ease-out}.admin__page-nav-item:hover{border-color:#e4e4e4}.admin__page-nav-item:hover .admin__page-nav-link{background:#e4e4e4;color:#303030;text-decoration:none}.admin__page-nav-item._active,.admin__page-nav-item.ui-state-active{border-color:#eb5202}.admin__page-nav-item._active .admin__page-nav-link,.admin__page-nav-item.ui-state-active .admin__page-nav-link{background:#fff;border-color:#e3e3e3;border-right:1px solid #fff;color:#303030;margin-right:-1px;font-weight:600}.admin__page-nav-item._loading:before,.admin__page-nav-item.ui-tabs-loading:before{display:none}.admin__page-nav-item._loading .admin__page-nav-item-message-loader,.admin__page-nav-item.ui-tabs-loading .admin__page-nav-item-message-loader{display:inline-block}.admin__page-nav-link{border:1px solid transparent;border-width:1px 0;color:#303030;display:block;font-weight:500;line-height:1.2;margin:0 0 -1px;padding:2rem 4rem 2rem 1rem;transition:border-color .1s ease-out,background-color .1s ease-out;word-wrap:break-word}.admin__page-nav-item-messages{display:inline-block}.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip{background:#f1f1f1;border:1px solid #f1f1f1;border-radius:1px;bottom:3.7rem;box-shadow:0 3px 9px 0 rgba(0,0,0,.3);display:none;font-size:1.4rem;font-weight:400;left:-1rem;line-height:1.36;padding:1.5rem;position:absolute;text-transform:none;width:27rem;word-break:normal;z-index:2}.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip:after,.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip:before{border:15px solid transparent;height:0;width:0;border-top-color:#f1f1f1;content:'';display:block;left:2rem;position:absolute;top:100%;z-index:3}.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip:after{border-top-color:#f1f1f1;margin-top:-1px;z-index:4}.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip:before{border-top-color:#bfbfbf;margin-top:1px}.admin__page-nav-item-message-loader{display:none;margin-top:-1rem;position:absolute;right:0;top:50%}.admin__page-nav-item-message-loader .spinner{font-size:2rem;margin-right:1.5rem}._loading>.admin__page-nav-item-messages .admin__page-nav-item-message-loader{display:inline-block}.admin__page-nav-item-message{position:relative}.admin__page-nav-item-message:hover{z-index:500}.admin__page-nav-item-message:hover .admin__page-nav-item-message-tooltip{display:block}.admin__page-nav-item-message._changed,.admin__page-nav-item-message._error{display:none}.admin__page-nav-item-message .admin__page-nav-item-message-icon{display:inline-block;font-size:1.4rem;padding-left:.8em;vertical-align:baseline}.admin__page-nav-item-message .admin__page-nav-item-message-icon:after{color:#666;content:'\e631'}._changed:not(._error)>.admin__page-nav-item-messages ._changed{display:inline-block}._error .admin__page-nav-item-message-icon:after{color:#eb5202;content:'\e623'}._error>.admin__page-nav-item-messages ._error{display:inline-block}._error>.admin__page-nav-item-messages ._error .spinner{font-size:2rem;margin-right:1.5rem}._error .admin__page-nav-item-message-tooltip{background:#f1f1f1;border:1px solid #f1f1f1;border-radius:1px;bottom:3.7rem;box-shadow:0 3px 9px 0 rgba(0,0,0,.3);display:none;font-weight:400;left:-1rem;line-height:1.36;padding:2rem;position:absolute;text-transform:none;width:27rem;word-break:normal;z-index:2}._error .admin__page-nav-item-message-tooltip:after,._error .admin__page-nav-item-message-tooltip:before{border:15px solid transparent;height:0;width:0;border-top-color:#f1f1f1;content:'';display:block;left:2rem;position:absolute;top:100%;z-index:3}._error .admin__page-nav-item-message-tooltip:after{border-top-color:#f1f1f1;margin-top:-1px;z-index:4}._error .admin__page-nav-item-message-tooltip:before{border-top-color:#bfbfbf}.admin__data-grid-wrap-static .data-grid{box-sizing:border-box}.admin__data-grid-wrap-static .data-grid thead{color:#333}.admin__data-grid-wrap-static .data-grid tr:nth-child(even) td{background-color:#f5f5f5}.admin__data-grid-wrap-static .data-grid tr:nth-child(even) td._dragging{background-color:rgba(245,245,245,.95)}.admin__data-grid-wrap-static .data-grid ul{margin-left:1rem;padding-left:1rem}.admin__data-grid-wrap-static .admin__data-grid-loading-mask{background:rgba(255,255,255,.5);bottom:0;left:0;position:absolute;right:0;top:0;z-index:399}.admin__data-grid-wrap-static .admin__data-grid-loading-mask .grid-loader{background:url(../images/loader-2.gif) 50% 50% no-repeat;bottom:0;height:149px;left:0;margin:auto;position:absolute;right:0;top:0;width:218px}.data-grid-filters-actions-wrap{float:right}.data-grid-search-control-wrap{float:left;max-width:45.5rem;position:relative;width:35%}.data-grid-search-control-wrap :-ms-input-placeholder{font-style:italic}.data-grid-search-control-wrap ::-webkit-input-placeholder{font-style:italic}.data-grid-search-control-wrap ::-moz-placeholder{font-style:italic}.data-grid-search-control-wrap .action-submit{background-color:transparent;border:none;border-radius:0;box-shadow:none;margin:0;padding:.6rem 2rem .2rem;position:absolute;right:0;top:1px}.data-grid-search-control-wrap .action-submit:hover{background-color:transparent;border:none;box-shadow:none}.data-grid-search-control-wrap .action-submit:active{-ms-transform:scale(0.9);transform:scale(0.9)}.data-grid-search-control-wrap .action-submit:hover:before{color:#1a1a1a}._keyfocus .data-grid-search-control-wrap .action-submit:focus{box-shadow:0 0 0 1px #008bdb}.data-grid-search-control-wrap .action-submit:before{content:'\e60c';font-size:2rem;transition:color .1s linear}.data-grid-search-control-wrap .action-submit>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.data-grid-search-control-wrap .abs-action-menu .action-submenu,.data-grid-search-control-wrap .abs-action-menu .action-submenu .action-submenu,.data-grid-search-control-wrap .action-menu,.data-grid-search-control-wrap .action-menu .action-submenu,.data-grid-search-control-wrap .actions-split .action-menu .action-submenu,.data-grid-search-control-wrap .actions-split .action-menu .action-submenu .action-submenu,.data-grid-search-control-wrap .actions-split .dropdown-menu .action-submenu,.data-grid-search-control-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{max-height:19.25rem;overflow-y:auto;z-index:398}.data-grid-search-control-wrap .action-menu-item._selected{background-color:#e0f6fe}.data-grid-search-control-wrap .data-grid-search-label{display:none}.data-grid-search-control{padding-right:6rem;width:100%}.data-grid-filters-action-wrap{float:left;padding-left:2rem}.data-grid-filters-action-wrap .action-default{font-size:1.3rem;margin-bottom:1rem;padding-left:1.7rem;padding-right:2.1rem;padding-top:.7rem}.data-grid-filters-action-wrap .action-default._active{background-color:#fff;border-bottom-color:#fff;border-right-color:#ccc;font-weight:600;margin:-.1rem 0 0;padding-bottom:1.6rem;padding-top:.8rem;position:relative;z-index:281}.data-grid-filters-action-wrap .action-default._active:after{background-color:#eb5202;bottom:100%;content:'';height:3px;left:-1px;position:absolute;right:-1px}.data-grid-filters-action-wrap .action-default:before{color:#333;content:'\e605';font-size:1.8rem;margin-right:.4rem;position:relative;top:-1px;vertical-align:top}.data-grid-filters-action-wrap .filters-active{display:none}.admin__action-grid-select .admin__control-select{margin:-.5rem .5rem 0 0;padding-bottom:.6rem;padding-top:.6rem}.admin__data-grid-filters-wrap{opacity:0;visibility:hidden;clear:both;font-size:1.3rem;transition:opacity .3s ease}.admin__data-grid-filters-wrap._show{opacity:1;visibility:visible;border-bottom:1px solid #ccc;border-top:1px solid #ccc;margin-bottom:.7rem;padding:3.6rem 0 3rem;position:relative;top:-1px;z-index:280}.admin__data-grid-filters-wrap._show .admin__data-grid-filters,.admin__data-grid-filters-wrap._show .admin__data-grid-filters-footer{display:block}.admin__data-grid-filters-wrap .admin__form-field-label,.admin__data-grid-filters-wrap .admin__form-field-legend{display:block;font-weight:700;margin:0 0 .3rem;text-align:left}.admin__data-grid-filters-wrap .admin__form-field{display:inline-block;margin-bottom:2em;margin-left:0;padding-left:2rem;padding-right:2rem;vertical-align:top;width:calc(100% / 4 - 4px)}.admin__data-grid-filters-wrap .admin__form-field .admin__form-field{display:block;float:none;margin-bottom:1.5rem;padding-left:0;padding-right:0;width:auto}.admin__data-grid-filters-wrap .admin__form-field .admin__form-field:last-child{margin-bottom:0}.admin__data-grid-filters-wrap .admin__form-field .admin__form-field .admin__form-field-label{border:1px solid transparent;float:left;font-weight:400;line-height:1.36;margin-bottom:0;padding-bottom:.6rem;padding-right:1em;padding-top:.6rem;width:25%}.admin__data-grid-filters-wrap .admin__form-field .admin__form-field .admin__form-field-control{margin-left:25%}.admin__data-grid-filters-wrap .admin__action-multiselect,.admin__data-grid-filters-wrap .admin__control-select,.admin__data-grid-filters-wrap .admin__control-text,.admin__data-grid-filters-wrap .admin__form-field-label{font-size:1.3rem}.admin__data-grid-filters-wrap .admin__control-select{height:3.2rem;padding-top:.5rem}.admin__data-grid-filters-wrap .admin__action-multiselect:before{height:3.2rem;width:3.2rem}.admin__data-grid-filters-wrap .admin__control-select,.admin__data-grid-filters-wrap .admin__control-text._has-datepicker{width:100%}.admin__data-grid-filters{display:none;margin-left:-2rem;margin-right:-2rem}.admin__filters-legend{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.admin__data-grid-filters-footer{display:none;font-size:1.4rem}.admin__data-grid-filters-footer .admin__footer-main-actions{margin-left:25%;text-align:right}.admin__data-grid-filters-footer .admin__footer-secondary-actions{float:left;width:50%}.admin__data-grid-filters-current{border-bottom:.1rem solid #ccc;border-top:.1rem solid #ccc;display:none;font-size:1.3rem;margin-bottom:.9rem;padding-bottom:.8rem;padding-top:1.1rem;width:100%}.admin__data-grid-filters-current._show{display:table;position:relative;top:-1px;z-index:3}.admin__data-grid-filters-current._show+.admin__data-grid-filters-wrap._show{margin-top:-1rem}.admin__current-filters-actions-wrap,.admin__current-filters-list-wrap,.admin__current-filters-title-wrap{display:table-cell;vertical-align:top}.admin__current-filters-title{margin-right:1em;white-space:nowrap}.admin__current-filters-list-wrap{width:100%}.admin__current-filters-list{margin-bottom:0}.admin__current-filters-list>li{display:inline-block;font-weight:600;margin:0 1rem .5rem;padding-right:2.6rem;position:relative}.admin__current-filters-list .action-remove{background-color:transparent;border:none;border-radius:0;box-shadow:none;margin:0;padding:0;line-height:1;position:absolute;right:0;top:1px}.admin__current-filters-list .action-remove:hover{background-color:transparent;border:none;box-shadow:none}.admin__current-filters-list .action-remove:hover:before{color:#949494}.admin__current-filters-list .action-remove:active{-ms-transform:scale(0.9);transform:scale(0.9)}.admin__current-filters-list .action-remove:before{color:#adadad;content:'\e620';font-size:1.6rem;transition:color .1s linear}.admin__current-filters-list .action-remove>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.admin__current-filters-actions-wrap .action-clear{border:none;padding-bottom:0;padding-top:0;white-space:nowrap}.admin__data-grid-pager-wrap{float:right;text-align:right}.admin__data-grid-pager{display:inline-block;margin-left:3rem}.admin__data-grid-pager .admin__control-text::-webkit-inner-spin-button,.admin__data-grid-pager .admin__control-text::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.admin__data-grid-pager .admin__control-text{-moz-appearance:textfield;text-align:center;width:4.4rem}.action-next,.action-previous{width:4.4rem}.action-next:before,.action-previous:before{font-weight:700}.action-next>span,.action-previous>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.action-previous{margin-right:2.5rem;text-indent:-.25em}.action-previous:before{content:'\e629'}.action-next{margin-left:1.5rem;text-indent:.1em}.action-next:before{content:'\e62a'}.admin__data-grid-action-bookmarks{opacity:.98}.admin__data-grid-action-bookmarks .admin__action-dropdown-text:after{left:0;right:-6px}.admin__data-grid-action-bookmarks._active{z-index:290}.admin__data-grid-action-bookmarks .admin__action-dropdown .admin__action-dropdown-text{display:inline-block;max-width:15rem;min-width:4.9rem;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.admin__data-grid-action-bookmarks .admin__action-dropdown:before{content:'\e60f'}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu{font-size:1.3rem;left:0;padding:1rem 0;right:auto}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu>li{padding:0 5rem 0 0;position:relative;white-space:nowrap}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu>li:not(.action-dropdown-menu-action){transition:background-color .1s linear}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu>li:not(.action-dropdown-menu-action):hover{background-color:#e3e3e3}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item{max-width:23rem;min-width:18rem;white-space:normal;word-break:break-all}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item-edit{display:none;padding-bottom:1rem;padding-left:1rem;padding-top:1rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item-edit .action-dropdown-menu-item-actions{padding-bottom:1rem;padding-top:1rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-action{padding-left:1rem;padding-top:1rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-action+.action-dropdown-menu-item-last{padding-top:.5rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-action>a{color:#008bdb;text-decoration:none;display:inline-block;padding-left:1.1rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-action>a:hover{color:#0fa7ff;text-decoration:underline}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item-last{padding-bottom:0}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu ._edit .action-dropdown-menu-item{display:none}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu ._edit .action-dropdown-menu-item-edit{display:block}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu ._active .action-dropdown-menu-link{font-weight:600}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .admin__control-text{font-size:1.3rem;min-width:15rem;width:calc(100% - 4rem)}.ie9 .admin__data-grid-action-bookmarks .admin__action-dropdown-menu .admin__control-text{width:15rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item-actions{border-left:1px solid #fff;bottom:0;position:absolute;right:0;top:0;width:5rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-link{color:#333;display:block;text-decoration:none;padding:1rem 1rem 1rem 2.1rem}.admin__data-grid-action-bookmarks .action-delete,.admin__data-grid-action-bookmarks .action-edit,.admin__data-grid-action-bookmarks .action-submit{background-color:transparent;border:none;border-radius:0;box-shadow:none;margin:0;vertical-align:top}.admin__data-grid-action-bookmarks .action-delete:hover,.admin__data-grid-action-bookmarks .action-edit:hover,.admin__data-grid-action-bookmarks .action-submit:hover{background-color:transparent;border:none;box-shadow:none}.admin__data-grid-action-bookmarks .action-delete:before,.admin__data-grid-action-bookmarks .action-edit:before,.admin__data-grid-action-bookmarks .action-submit:before{font-size:1.7rem}.admin__data-grid-action-bookmarks .action-delete>span,.admin__data-grid-action-bookmarks .action-edit>span,.admin__data-grid-action-bookmarks .action-submit>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.admin__data-grid-action-bookmarks .action-delete,.admin__data-grid-action-bookmarks .action-edit{padding:.6rem 1.4rem}.admin__data-grid-action-bookmarks .action-delete:active,.admin__data-grid-action-bookmarks .action-edit:active{-ms-transform:scale(0.9);transform:scale(0.9)}.admin__data-grid-action-bookmarks .action-submit{padding:.6rem 1rem .6rem .8rem}.admin__data-grid-action-bookmarks .action-submit:active{position:relative;right:-1px}.admin__data-grid-action-bookmarks .action-submit:before{content:'\e625'}.admin__data-grid-action-bookmarks .action-delete:before{content:'\e630'}.admin__data-grid-action-bookmarks .action-edit{padding-top:.8rem}.admin__data-grid-action-bookmarks .action-edit:before{content:'\e631'}.admin__data-grid-action-columns._active{opacity:.98;z-index:290}.admin__data-grid-action-columns .admin__action-dropdown:before{content:'\e610';font-size:1.8rem;margin-right:.7rem;vertical-align:top}.admin__data-grid-action-columns-menu{color:#303030;font-size:1.3rem;overflow:hidden;padding:2.2rem 3.5rem 1rem;z-index:1}.admin__data-grid-action-columns-menu._overflow .admin__action-dropdown-menu-header{border-bottom:1px solid #d1d1d1}.admin__data-grid-action-columns-menu._overflow .admin__action-dropdown-menu-content{width:49.2rem}.admin__data-grid-action-columns-menu._overflow .admin__action-dropdown-menu-footer{border-top:1px solid #d1d1d1;padding-top:2.5rem}.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-content{max-height:22.85rem;overflow-y:auto;padding-top:1.5rem;position:relative;width:47.4rem}.admin__data-grid-action-columns-menu .admin__field-option{float:left;height:1.9rem;margin-bottom:1.5rem;padding:0 1rem 0 0;width:15.8rem}.admin__data-grid-action-columns-menu .admin__field-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-header{padding-bottom:1.5rem}.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-footer{padding:1rem 0 2rem}.admin__data-grid-action-columns-menu .admin__action-dropdown-footer-main-actions{margin-left:25%;text-align:right}.admin__data-grid-action-columns-menu .admin__action-dropdown-footer-secondary-actions{float:left;margin-left:-1em}.admin__data-grid-action-export._active{opacity:.98;z-index:290}.admin__data-grid-action-export .admin__action-dropdown:before{content:'\e635';font-size:1.7rem;left:.3rem;margin-right:.7rem;vertical-align:top}.admin__data-grid-action-export-menu{padding-left:2rem;padding-right:2rem;padding-top:1rem}.admin__data-grid-action-export-menu .admin__action-dropdown-footer-main-actions{padding-bottom:2rem;padding-top:2.5rem;white-space:nowrap}.sticky-header{background-color:#f8f8f8;border-bottom:1px solid #e3e3e3;box-shadow:0 5px 5px 0 rgba(0,0,0,.25);left:8.8rem;margin-top:-1px;padding:.5rem 3rem 0;position:fixed;right:0;top:77px;z-index:398}.sticky-header .admin__data-grid-wrap{margin-bottom:0;overflow-x:visible;padding-bottom:0}.sticky-header .admin__data-grid-header-row{position:relative;text-align:right}.sticky-header .admin__data-grid-header-row:last-child{margin:0}.sticky-header .admin__data-grid-actions-wrap,.sticky-header .admin__data-grid-filters-wrap,.sticky-header .admin__data-grid-pager-wrap,.sticky-header .data-grid-filters-actions-wrap,.sticky-header .data-grid-search-control-wrap{display:inline-block;float:none;vertical-align:top}.sticky-header .action-select-wrap{float:left;margin-right:1.5rem;width:16.66666667%}.sticky-header .admin__control-support-text{float:left}.sticky-header .data-grid-search-control-wrap{margin:-.5rem 0 0 1.1rem;width:auto}.sticky-header .data-grid-search-control-wrap .data-grid-search-label{box-sizing:border-box;cursor:pointer;display:block;min-width:3.8rem;padding:1.2rem .6rem 1.7rem;position:relative;text-align:center}.sticky-header .data-grid-search-control-wrap .data-grid-search-label:before{color:#333;content:'\e60c';font-size:2rem;transition:color .1s linear}.sticky-header .data-grid-search-control-wrap .data-grid-search-label:hover:before{color:#000}.sticky-header .data-grid-search-control-wrap .data-grid-search-label span{display:none}.sticky-header .data-grid-filters-actions-wrap{margin:-.5rem 0 0 1.1rem;padding-left:0;position:relative}.sticky-header .data-grid-filters-actions-wrap .action-default{background-color:transparent;border:1px solid transparent;box-sizing:border-box;min-width:3.8rem;padding:1.2rem .6rem 1.7rem;text-align:center;transition:all .15s ease}.sticky-header .data-grid-filters-actions-wrap .action-default span{display:none}.sticky-header .data-grid-filters-actions-wrap .action-default:before{margin:0}.sticky-header .data-grid-filters-actions-wrap .action-default._active{background-color:#fff;border-color:#adadad #adadad #fff;box-shadow:1px 1px 5px rgba(0,0,0,.5);z-index:210}.sticky-header .data-grid-filters-actions-wrap .action-default._active:after{background-color:#fff;content:'';height:6px;left:-2px;position:absolute;right:-6px;top:100%}.sticky-header .data-grid-filters-action-wrap{padding:0}.sticky-header .admin__data-grid-filters-wrap{background-color:#fff;border:1px solid #adadad;box-shadow:0 5px 5px 0 rgba(0,0,0,.25);left:0;padding-left:3.5rem;padding-right:3.5rem;position:absolute;top:100%;width:100%;z-index:209}.sticky-header .admin__data-grid-filters-current+.admin__data-grid-filters-wrap._show{margin-top:-6px}.sticky-header .filters-active{background-color:#e04f00;border-radius:10px;color:#fff;display:block;font-size:1.4rem;font-weight:700;padding:.1rem .7rem;position:absolute;right:-7px;top:0;z-index:211}.sticky-header .filters-active:empty{padding-bottom:0;padding-top:0}.sticky-header .admin__data-grid-actions-wrap{margin:-.5rem 0 0 1.1rem;padding-right:.3rem}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown{background-color:transparent;box-sizing:border-box;min-width:3.8rem;padding-left:.6rem;padding-right:.6rem;text-align:center}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown .admin__action-dropdown-text{display:inline-block;max-width:0;min-width:0;overflow:hidden}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown:before{margin:0}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown-wrap{margin-right:1.1rem}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown-wrap:after,.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown:after{display:none}.sticky-header .admin__data-grid-actions-wrap ._active .admin__action-dropdown{background-color:#fff}.sticky-header .admin__data-grid-action-bookmarks .admin__action-dropdown:before{position:relative;top:-3px}.sticky-header .admin__data-grid-filters-current{border-bottom:0;border-top:0;margin-bottom:0;padding-bottom:0;padding-top:0}.sticky-header .admin__data-grid-pager .admin__control-text,.sticky-header .admin__data-grid-pager-wrap .admin__control-support-text,.sticky-header .data-grid-search-control-wrap .action-submit,.sticky-header .data-grid-search-control-wrap .data-grid-search-control{display:none}.sticky-header .action-next{margin:0}.sticky-header .data-grid{margin-bottom:-1px}.data-grid-cap-left,.data-grid-cap-right{background-color:#f8f8f8;bottom:-2px;position:absolute;top:6rem;width:3rem;z-index:201}.data-grid-cap-left{left:0}.admin__data-grid-header{font-size:1.4rem}.admin__data-grid-header-row+.admin__data-grid-header-row{margin-top:1.1rem}.admin__data-grid-header-row:last-child{margin-bottom:0}.admin__data-grid-header-row .action-select-wrap{display:block}.admin__data-grid-header-row .action-select{width:100%}.admin__data-grid-actions-wrap{float:right;margin-left:1.1rem;margin-top:-.5rem;text-align:right}.admin__data-grid-actions-wrap .admin__action-dropdown-wrap{position:relative;text-align:left;vertical-align:middle}.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._active+.admin__action-dropdown-wrap:after,.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._active:after,.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._hide+.admin__action-dropdown-wrap:after,.admin__data-grid-actions-wrap .admin__action-dropdown-wrap:first-child:after{display:none}.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._active .admin__action-dropdown,.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._active .admin__action-dropdown-menu{border-color:#adadad}.admin__data-grid-actions-wrap .admin__action-dropdown-wrap:after{border-left:1px solid #ccc;content:'';height:3.2rem;left:0;position:absolute;top:.5rem;z-index:3}.admin__data-grid-actions-wrap .admin__action-dropdown{padding-bottom:1.7rem;padding-top:1.2rem}.admin__data-grid-actions-wrap .admin__action-dropdown:after{margin-top:-.4rem}.admin__data-grid-outer-wrap{min-height:8rem;position:relative}.admin__data-grid-wrap{margin-bottom:2rem;max-width:100%;overflow-x:auto;padding-bottom:1rem;padding-top:2rem}.admin__data-grid-loading-mask{background:rgba(255,255,255,.5);bottom:0;left:0;position:absolute;right:0;top:0;z-index:399}.admin__data-grid-loading-mask .spinner{font-size:4rem;left:50%;margin-left:-2rem;margin-top:-2rem;position:absolute;top:50%}.ie9 .admin__data-grid-loading-mask .spinner{background:url(../images/loader-2.gif) 50% 50% no-repeat;bottom:0;height:149px;left:0;margin:auto;position:absolute;right:0;top:0;width:218px}.data-grid-cell-content{display:inline-block;overflow:hidden;width:100%}body._in-resize{cursor:col-resize;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body._in-resize *,body._in-resize .data-grid-th,body._in-resize .data-grid-th._draggable,body._in-resize .data-grid-th._sortable{cursor:col-resize!important}._layout-fixed{table-layout:fixed}.data-grid{border:none;font-size:1.3rem;margin-bottom:0;width:100%}.data-grid:not(._dragging-copy) ._odd-row td._dragging{background-color:#d0d0d0}.data-grid:not(._dragging-copy) ._dragging{background-color:#d9d9d9;color:rgba(48,48,48,.95)}.data-grid:not(._dragging-copy) ._dragging a{color:rgba(0,139,219,.95)}.data-grid:not(._dragging-copy) ._dragging a:hover{color:rgba(15,167,255,.95)}.data-grid._dragged{outline:#007bdb solid 1px}.data-grid thead{background-color:transparent}.data-grid tfoot th{padding:1rem}.data-grid tr._odd-row td{background-color:#f5f5f5}.data-grid tr._odd-row td._update-status-active{background:#89e1ff}.data-grid tr._odd-row td._update-status-upcoming{background:#b7ee63}.data-grid tr:hover td._update-status-active,.data-grid tr:hover td._update-status-upcoming{background-color:#e5f7fe}.data-grid tr.data-grid-tr-no-data td{font-size:1.6rem;padding:3rem;text-align:center}.data-grid tr.data-grid-tr-no-data:hover td{background-color:#fff;cursor:default}.data-grid tr:active td{background-color:#e0f6fe}.data-grid tr:hover td{background-color:#e5f7fe}.data-grid tr._dragged td{background:#d0d0d0}.data-grid tr._dragover-top td{box-shadow:inset 0 3px 0 0 #008bdb}.data-grid tr._dragover-bottom td{box-shadow:inset 0 -3px 0 0 #008bdb}.data-grid tr:not(.data-grid-editable-row):last-child td{border-bottom:.1rem solid #d6d6d6}.data-grid tr ._clickable,.data-grid tr._clickable{cursor:pointer}.data-grid tr._disabled{pointer-events:none}.data-grid td,.data-grid th{font-size:1.3rem;line-height:1.36;transition:background-color .1s linear;vertical-align:top}.data-grid td._resizing,.data-grid th._resizing{border-left:1px solid #007bdb;border-right:1px solid #007bdb}.data-grid td._hidden,.data-grid th._hidden{display:none}.data-grid td._fit,.data-grid th._fit{width:1%}.data-grid td{background-color:#fff;border-left:.1rem dashed #d6d6d6;border-right:.1rem dashed #d6d6d6;color:#303030;padding:1rem}.data-grid td:first-child{border-left-style:solid}.data-grid td:last-child{border-right-style:solid}.data-grid td .action-select-wrap{position:static}.data-grid td .action-select{color:#008bdb;text-decoration:none;background-color:transparent;border:none;font-size:1.3rem;padding:0 3rem 0 0;position:relative}.data-grid td .action-select:hover{color:#0fa7ff;text-decoration:underline}.data-grid td .action-select:hover:after{border-color:#0fa7ff transparent transparent}.data-grid td .action-select:after{border-color:#008bdb transparent transparent;margin:.6rem 0 0 .7rem;right:auto;top:auto}.data-grid td .action-select:before{display:none}.data-grid td .abs-action-menu .action-submenu,.data-grid td .abs-action-menu .action-submenu .action-submenu,.data-grid td .action-menu,.data-grid td .action-menu .action-submenu,.data-grid td .actions-split .action-menu .action-submenu,.data-grid td .actions-split .action-menu .action-submenu .action-submenu,.data-grid td .actions-split .dropdown-menu .action-submenu,.data-grid td .actions-split .dropdown-menu .action-submenu .action-submenu{left:auto;min-width:10rem;right:0;text-align:left;top:auto;z-index:1}.data-grid td._update-status-active{background:#bceeff}.data-grid td._update-status-upcoming{background:#ccf391}.data-grid th{background-color:#514943;border:.1rem solid #8a837f;border-left-color:transparent;color:#fff;font-weight:600;padding:0;text-align:left}.data-grid th:first-child{border-left-color:#8a837f}.data-grid th._dragover-left{box-shadow:inset 3px 0 0 0 #fff;z-index:2}.data-grid th._dragover-right{box-shadow:inset -3px 0 0 0 #fff}.data-grid .shadow-div{cursor:col-resize;height:100%;margin-right:-5px;position:absolute;right:0;top:0;width:10px}.data-grid .data-grid-th{background-clip:padding-box;color:#fff;padding:1rem;position:relative;vertical-align:middle}.data-grid .data-grid-th._resize-visible .shadow-div{cursor:auto;display:none}.data-grid .data-grid-th._draggable{cursor:grab}.data-grid .data-grid-th._sortable{cursor:pointer;transition:background-color .1s linear;z-index:1}.data-grid .data-grid-th._sortable:focus,.data-grid .data-grid-th._sortable:hover{background-color:#5f564f}.data-grid .data-grid-th._sortable:active{padding-bottom:.9rem;padding-top:1.1rem}.data-grid .data-grid-th.required>span:after{color:#f38a5e;content:'*';margin-left:.3rem}.data-grid .data-grid-checkbox-cell{overflow:hidden;padding:0;vertical-align:top;width:5.2rem}.data-grid .data-grid-checkbox-cell:hover{cursor:default}.data-grid .data-grid-thumbnail-cell{text-align:center;width:7rem}.data-grid .data-grid-thumbnail-cell img{border:1px solid #d6d6d6;width:5rem}.data-grid .data-grid-multicheck-cell{padding:1rem 1rem .9rem;text-align:center;vertical-align:middle}.data-grid .data-grid-onoff-cell{text-align:center;width:12rem}.data-grid .data-grid-actions-cell{padding-left:2rem;padding-right:2rem;text-align:center;width:1%}.data-grid._hidden{display:none}.data-grid._dragging-copy{box-shadow:1px 1px 5px rgba(0,0,0,.5);left:0;opacity:.95;position:fixed;top:0;z-index:1000}.data-grid._dragging-copy .data-grid-th{border:1px solid #007bdb;border-bottom:none}.data-grid._dragging-copy .data-grid-th,.data-grid._dragging-copy .data-grid-th._sortable{cursor:grabbing}.data-grid._dragging-copy tr:last-child td{border-bottom:1px solid #007bdb}.data-grid._dragging-copy td{border-left:1px solid #007bdb;border-right:1px solid #007bdb}.data-grid._dragging-copy._in-edit .data-grid-editable-row.data-grid-bulk-edit-panel td,.data-grid._dragging-copy._in-edit .data-grid-editable-row.data-grid-bulk-edit-panel td:before,.data-grid._dragging-copy._in-edit .data-grid-editable-row.data-grid-bulk-edit-panel:hover td{background-color:rgba(255,251,230,.95)}.data-grid._dragging-copy._in-edit .data-grid-editable-row td,.data-grid._dragging-copy._in-edit .data-grid-editable-row:hover td{background-color:rgba(255,255,255,.95)}.data-grid._dragging-copy._in-edit .data-grid-editable-row td:after,.data-grid._dragging-copy._in-edit .data-grid-editable-row td:before{left:0;right:0}.data-grid._dragging-copy._in-edit .data-grid-editable-row td:before{background-color:rgba(255,255,255,.95)}.data-grid._dragging-copy._in-edit .data-grid-editable-row td:only-child{border-left:1px solid #007bdb;border-right:1px solid #007bdb;left:0}.data-grid._dragging-copy._in-edit .data-grid-editable-row .admin__control-select,.data-grid._dragging-copy._in-edit .data-grid-editable-row .admin__control-text{opacity:.5}.data-grid .data-grid-controls-row td{padding-top:1.6rem}.data-grid .data-grid-controls-row td.data-grid-checkbox-cell{padding-top:.6rem}.data-grid .data-grid-controls-row td [class*=admin__control-],.data-grid .data-grid-controls-row td button{margin-top:-1.7rem}.data-grid._in-edit tr:hover td{background-color:#e6e6e6}.data-grid._in-edit ._odd-row.data-grid-editable-row td,.data-grid._in-edit ._odd-row.data-grid-editable-row:hover td{background-color:#fff}.data-grid._in-edit ._odd-row td,.data-grid._in-edit ._odd-row:hover td{background-color:#dcdcdc}.data-grid._in-edit .data-grid-editable-row-actions td,.data-grid._in-edit .data-grid-editable-row-actions:hover td{background-color:#fff}.data-grid._in-edit td{background-color:#e6e6e6;pointer-events:none}.data-grid._in-edit .data-grid-checkbox-cell{pointer-events:auto}.data-grid._in-edit .data-grid-editable-row{border:.1rem solid #adadad;border-bottom-color:#c2c2c2}.data-grid._in-edit .data-grid-editable-row:hover td{background-color:#fff}.data-grid._in-edit .data-grid-editable-row td{background-color:#fff;border-bottom-color:#fff;border-left-style:hidden;border-right-style:hidden;border-top-color:#fff;pointer-events:auto;vertical-align:middle}.data-grid._in-edit .data-grid-editable-row td:first-child{border-left-color:#adadad;border-left-style:solid}.data-grid._in-edit .data-grid-editable-row td:first-child:after,.data-grid._in-edit .data-grid-editable-row td:first-child:before{left:0}.data-grid._in-edit .data-grid-editable-row td:last-child{border-right-color:#adadad;border-right-style:solid;left:-.1rem}.data-grid._in-edit .data-grid-editable-row td:last-child:after,.data-grid._in-edit .data-grid-editable-row td:last-child:before{right:0}.data-grid._in-edit .data-grid-editable-row .admin__control-select,.data-grid._in-edit .data-grid-editable-row .admin__control-text{width:100%}.data-grid._in-edit .data-grid-bulk-edit-panel td{vertical-align:bottom}.data-grid .data-grid-editable-row td{border-left-color:#fff;border-left-style:solid;position:relative;z-index:1}.data-grid .data-grid-editable-row td:after{bottom:0;box-shadow:0 5px 5px rgba(0,0,0,.25);content:'';height:.9rem;left:0;margin-top:-1rem;position:absolute;right:0}.data-grid .data-grid-editable-row td:before{background-color:#fff;bottom:0;content:'';height:1rem;left:-10px;position:absolute;right:-10px;z-index:1}.data-grid .data-grid-editable-row.data-grid-editable-row-actions td,.data-grid .data-grid-editable-row.data-grid-editable-row-actions:hover td{background-color:#fff}.data-grid .data-grid-editable-row.data-grid-editable-row-actions td:first-child{border-left-color:#fff;border-right-color:#fff}.data-grid .data-grid-editable-row.data-grid-editable-row-actions td:last-child{left:0}.data-grid .data-grid-editable-row.data-grid-bulk-edit-panel td,.data-grid .data-grid-editable-row.data-grid-bulk-edit-panel td:before,.data-grid .data-grid-editable-row.data-grid-bulk-edit-panel:hover td{background-color:#fffbe6}.data-grid .data-grid-editable-row-actions{left:50%;margin-left:-12.5rem;margin-top:-2px;position:absolute;text-align:center}.data-grid .data-grid-editable-row-actions td{width:25rem}.data-grid .data-grid-editable-row-actions [class*=action-]{min-width:9rem}.data-grid .data-grid-draggable-row-cell{width:1%}.data-grid .data-grid-draggable-row-cell .draggable-handle{padding:0}.data-grid-th._sortable._ascend,.data-grid-th._sortable._descend{padding-right:2.7rem}.data-grid-th._sortable._ascend:before,.data-grid-th._sortable._descend:before{margin-top:-1em;position:absolute;right:1rem;top:50%}.data-grid-th._sortable._ascend:before{content:'\2193'}.data-grid-th._sortable._descend:before{content:'\2191'}.data-grid-checkbox-cell-inner{display:block;padding:1.1rem 1.8rem .9rem;text-align:right}.data-grid-checkbox-cell-inner:hover{cursor:pointer}.data-grid-state-cell-inner{display:block;padding:1.1rem 1.8rem .9rem;text-align:center}.data-grid-state-cell-inner>span{display:inline-block;font-style:italic;padding:.6rem 0}.data-grid-row-parent._active>td .data-grid-checkbox-cell-inner:before{content:'\e62b'}.data-grid-row-parent>td .data-grid-checkbox-cell-inner{padding-left:3.7rem;position:relative}.data-grid-row-parent>td .data-grid-checkbox-cell-inner:before{content:'\e628';font-size:1rem;font-weight:700;left:1.35rem;position:absolute;top:1.6rem}.data-grid-th._col-xs{width:1%}.data-grid-info-panel{box-shadow:0 0 5px rgba(0,0,0,.5);margin:2rem .1rem -2rem}.data-grid-info-panel .messages{overflow:hidden}.data-grid-info-panel .messages .message{margin:1rem}.data-grid-info-panel .messages .message:last-child{margin-bottom:1rem}.data-grid-info-panel-actions{padding:1rem;text-align:right}.data-grid-editable-row .admin__field-control{position:relative}.data-grid-editable-row .admin__field-control._error:after{border-color:transparent #ee7d7d transparent transparent;border-style:solid;border-width:0 12px 12px 0;content:'';position:absolute;right:0;top:0}.data-grid-editable-row .admin__field-control._error .admin__control-text{border-color:#ee7d7d}.data-grid-editable-row .admin__field-control._focus:after{display:none}.data-grid-editable-row .admin__field-error{bottom:100%;box-shadow:1px 1px 5px rgba(0,0,0,.5);left:0;margin:0 auto 1.5rem;max-width:32rem;position:absolute;right:0}.data-grid-editable-row .admin__field-error:after,.data-grid-editable-row .admin__field-error:before{border-style:solid;content:'';left:50%;position:absolute;top:100%}.data-grid-editable-row .admin__field-error:after{border-color:#fffbbb transparent transparent;border-width:10px 10px 0;margin-left:-10px;z-index:1}.data-grid-editable-row .admin__field-error:before{border-color:#ee7d7d transparent transparent;border-width:11px 12px 0;margin-left:-12px}.data-grid-bulk-edit-panel .admin__field-label-vertical{display:block;font-size:1.2rem;margin-bottom:.5rem;text-align:left}.data-grid-row-changed{cursor:default;display:block;opacity:.5;position:relative;width:100%;z-index:1}.data-grid-row-changed:after{content:'\e631';display:inline-block}.data-grid-row-changed .data-grid-row-changed-tooltip{background:#f1f1f1;border:1px solid #f1f1f1;border-radius:1px;bottom:100%;box-shadow:0 3px 9px 0 rgba(0,0,0,.3);display:none;font-weight:400;line-height:1.36;margin-bottom:1.5rem;padding:1rem;position:absolute;right:-1rem;text-transform:none;width:27rem;word-break:normal;z-index:2}.data-grid-row-changed._changed{opacity:1;z-index:3}.data-grid-row-changed._changed:hover .data-grid-row-changed-tooltip{display:block}.data-grid-row-changed._changed:hover:before{background:#f1f1f1;border:1px solid #f1f1f1;bottom:100%;box-shadow:4px 4px 3px -1px rgba(0,0,0,.15);content:'';display:block;height:1.6rem;left:50%;margin:0 0 .7rem -.8rem;position:absolute;-ms-transform:rotate(45deg);transform:rotate(45deg);width:1.6rem;z-index:3}.ie9 .data-grid-row-changed._changed:hover:before{display:none}.admin__data-grid-outer-wrap .data-grid-checkbox-cell{overflow:hidden}.admin__data-grid-outer-wrap .data-grid-checkbox-cell-inner{position:relative}.admin__data-grid-outer-wrap .data-grid-checkbox-cell-inner:before{bottom:0;content:'';height:500%;left:0;position:absolute;right:0;top:0}.admin__data-grid-wrap-static .data-grid-checkbox-cell:hover{cursor:pointer}.admin__data-grid-wrap-static .data-grid-checkbox-cell-inner{margin:1.1rem 1.8rem .9rem;padding:0}.adminhtml-cms-hierarchy-index .admin__data-grid-wrap-static .data-grid-actions-cell:first-child{padding:0}.adminhtml-export-index .admin__data-grid-wrap-static .data-grid-checkbox-cell-inner{margin:0;padding:1.1rem 1.8rem 1.9rem}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child:before,.admin__control-file-label:before,.admin__control-multiselect,.admin__control-select,.admin__control-text,.admin__control-textarea,.selectmenu{-webkit-appearance:none;background-color:#fff;border:1px solid #adadad;border-radius:1px;box-shadow:none;color:#303030;font-size:1.4rem;font-weight:400;height:auto;line-height:1.36;padding:.6rem 1rem;transition:border-color .1s linear;vertical-align:baseline;width:auto}.admin__control-addon [class*=admin__control-][class]:hover~[class*=admin__addon-]:last-child:before,.admin__control-multiselect:hover,.admin__control-select:hover,.admin__control-text:hover,.admin__control-textarea:hover,.selectmenu:hover,.selectmenu:hover .selectmenu-toggle:before{border-color:#878787}.admin__control-addon [class*=admin__control-][class]:focus~[class*=admin__addon-]:last-child:before,.admin__control-file:active+.admin__control-file-label:before,.admin__control-file:focus+.admin__control-file-label:before,.admin__control-multiselect:focus,.admin__control-select:focus,.admin__control-text:focus,.admin__control-textarea:focus,.selectmenu._focus,.selectmenu._focus .selectmenu-toggle:before{border-color:#007bdb;box-shadow:none;outline:0}.admin__control-addon [class*=admin__control-][class][disabled]~[class*=admin__addon-]:last-child:before,.admin__control-file[disabled]+.admin__control-file-label:before,.admin__control-multiselect[disabled],.admin__control-select[disabled],.admin__control-text[disabled],.admin__control-textarea[disabled]{background-color:#e9e9e9;border-color:#adadad;color:#303030;cursor:not-allowed;opacity:.5}.admin__field-row[class]>.admin__field-control,.admin__fieldset>.admin__field.admin__field-wide[class]>.admin__field-control{clear:left;float:none;text-align:left;width:auto}.admin__field-row[class]:not(.admin__field-option)>.admin__field-label,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option)>.admin__field-label{display:block;line-height:1.4rem;margin-bottom:.86rem;margin-top:-.14rem;text-align:left;width:auto}.admin__field-row[class]:not(.admin__field-option)>.admin__field-label:before,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option)>.admin__field-label:before{display:none}.admin__field-row[class]:not(.admin__field-option)._required>.admin__field-label span,.admin__field-row[class]:not(.admin__field-option).required>.admin__field-label span,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option)._required>.admin__field-label span,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option).required>.admin__field-label span{padding-left:1.5rem}.admin__field-row[class]:not(.admin__field-option)._required>.admin__field-label span:after,.admin__field-row[class]:not(.admin__field-option).required>.admin__field-label span:after,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option)._required>.admin__field-label span:after,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option).required>.admin__field-label span:after{left:0;margin-left:30px}.admin__legend{font-size:1.8rem;font-weight:600;margin-bottom:3rem}.admin__control-checkbox,.admin__control-radio{cursor:pointer;opacity:.01;overflow:hidden;position:absolute;vertical-align:top}.admin__control-checkbox:after,.admin__control-radio:after{display:none}.admin__control-checkbox+label,.admin__control-radio+label{cursor:pointer;display:inline-block}.admin__control-checkbox+label:before,.admin__control-radio+label:before{background-color:#fff;border:1px solid #adadad;color:transparent;float:left;height:1.6rem;text-align:center;vertical-align:top;width:1.6rem}.admin__control-checkbox+.admin__field-label,.admin__control-radio+.admin__field-label{padding-left:2.6rem}.admin__control-checkbox+.admin__field-label:before,.admin__control-radio+.admin__field-label:before{margin:1px 1rem 0 -2.6rem}.admin__control-checkbox:checked+label:before,.admin__control-radio:checked+label:before{color:#514943}.admin__control-checkbox.disabled+label,.admin__control-checkbox[disabled]+label,.admin__control-radio.disabled+label,.admin__control-radio[disabled]+label{color:#303030;cursor:default;opacity:.5}.admin__control-checkbox.disabled+label:before,.admin__control-checkbox[disabled]+label:before,.admin__control-radio.disabled+label:before,.admin__control-radio[disabled]+label:before{background-color:#e9e9e9;border-color:#adadad;cursor:default}._keyfocus .admin__control-checkbox:not(.disabled):focus+label:before,._keyfocus .admin__control-checkbox:not([disabled]):focus+label:before,._keyfocus .admin__control-radio:not(.disabled):focus+label:before,._keyfocus .admin__control-radio:not([disabled]):focus+label:before{border-color:#007bdb}.admin__control-checkbox:not(.disabled):hover+label:before,.admin__control-checkbox:not([disabled]):hover+label:before,.admin__control-radio:not(.disabled):hover+label:before,.admin__control-radio:not([disabled]):hover+label:before{border-color:#878787}.admin__control-radio+label:before{border-radius:1.6rem;content:'';transition:border-color .1s linear,color .1s ease-in}.admin__control-radio.admin__control-radio+label:before{line-height:140%}.admin__control-radio:checked+label{position:relative}.admin__control-radio:checked+label:after{background-color:#514943;border-radius:50%;content:'';height:10px;left:3px;position:absolute;top:4px;width:10px}.admin__control-radio:checked:not(.disabled):hover,.admin__control-radio:checked:not(.disabled):hover+label,.admin__control-radio:checked:not([disabled]):hover,.admin__control-radio:checked:not([disabled]):hover+label{cursor:default}.admin__control-radio:checked:not(.disabled):hover+label:before,.admin__control-radio:checked:not([disabled]):hover+label:before{border-color:#adadad}.admin__control-checkbox+label:before{border-radius:1px;content:'';font-size:0;transition:font-size .1s ease-out,color .1s ease-out,border-color .1s linear}.admin__control-checkbox:checked+label:before{content:'\e62d';font-size:1.1rem;line-height:125%}.admin__control-checkbox:not(:checked)._indeterminate+label:before,.admin__control-checkbox:not(:checked):indeterminate+label:before{color:#514943;content:'-';font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.4rem;font-weight:700}input[type=checkbox].admin__control-checkbox,input[type=radio].admin__control-checkbox{margin:0;position:absolute}.admin__control-text{min-width:4rem}.admin__control-select{-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none;background-image:url(../images/arrows-bg.svg),linear-gradient(#e3e3e3,#e3e3e3),linear-gradient(#adadad,#adadad);background-position:calc(100% - 12px) -34px,100%,calc(100% - 3.2rem) 0;background-size:auto,3.2rem 100%,1px 100%;background-repeat:no-repeat;max-width:100%;min-width:8.5rem;padding-bottom:.5rem;padding-right:4.4rem;padding-top:.5rem;transition:border-color .1s linear}.admin__control-select:hover{border-color:#878787;cursor:pointer}.admin__control-select:focus{background-image:url(../images/arrows-bg.svg),linear-gradient(#e3e3e3,#e3e3e3),linear-gradient(#007bdb,#007bdb);background-position:calc(100% - 12px) 13px,100%,calc(100% - 3.2rem) 0;border-color:#007bdb}.admin__control-select::-ms-expand{display:none}.ie9 .admin__control-select{background-image:none;padding-right:1rem}option:empty{display:none}.admin__control-multiselect{height:auto;max-width:100%;min-width:15rem;overflow:auto;padding:0;resize:both}.admin__control-multiselect optgroup,.admin__control-multiselect option{padding:.5rem 1rem}.admin__control-file-wrapper{display:inline-block;padding:.5rem 1rem;position:relative;z-index:1}.admin__control-file-label:before{content:'';left:0;position:absolute;top:0;width:100%;z-index:0}.admin__control-file{background:0 0;border:0;padding-top:.7rem;position:relative;width:auto;z-index:1}.admin__control-support-text{border:1px solid transparent;display:inline-block;font-size:1.4rem;line-height:1.36;padding-bottom:.6rem;padding-top:.6rem}.admin__control-support-text+[class*=admin__control-],[class*=admin__control-]+.admin__control-support-text{margin-left:.7rem}.admin__control-service{float:left;margin:.8rem 0 0 3rem}.admin__control-textarea{height:8.48rem;line-height:1.18;padding-top:.8rem;resize:vertical}.admin__control-addon{-ms-flex-direction:row;flex-direction:row;display:inline-flex;-ms-flex-flow:row nowrap;flex-flow:row nowrap;position:relative;width:100%;z-index:1}.admin__control-addon>[class*=admin__addon-],.admin__control-addon>[class*=admin__control-]{-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0;position:relative;z-index:1}.admin__control-addon .admin__control-select{width:auto}.admin__control-addon .admin__control-text{margin:.1rem;padding:.5rem .9rem;width:100%}.admin__control-addon [class*=admin__control-][class]{appearence:none;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-order:1;order:1;-ms-flex-negative:1;flex-shrink:1;background-color:transparent;border-color:transparent;box-shadow:none;vertical-align:top}.admin__control-addon [class*=admin__control-][class]+[class*=admin__control-]{border-left-color:#adadad}.admin__control-addon [class*=admin__control-][class] :focus{box-shadow:0}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child{padding-left:1rem;position:static!important;z-index:0}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child>*{position:relative;vertical-align:top;z-index:1}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child:empty{padding:0}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child:before{bottom:0;box-sizing:border-box;content:'';left:0;position:absolute;top:0;width:100%;z-index:-1}.admin__addon-prefix,.admin__addon-suffix{border:0;box-sizing:border-box;color:#858585;display:inline-block;font-size:1.4rem;font-weight:400;height:3.2rem;line-height:3.2rem;padding:0}.admin__addon-suffix{-ms-flex-order:3;order:3}.admin__addon-suffix:last-child{padding-right:1rem}.admin__addon-prefix{-ms-flex-order:0;order:0}.ie9 .admin__control-addon:after{clear:both;content:'';display:block;height:0;overflow:hidden}.ie9 .admin__addon{min-width:0;overflow:hidden;text-align:right;white-space:nowrap;width:auto}.ie9 .admin__addon [class*=admin__control-]{display:inline}.ie9 .admin__addon-prefix{float:left}.ie9 .admin__addon-suffix{float:right}.admin__control-collapsible{width:100%}.admin__control-collapsible ._dragged .admin__collapsible-block-wrapper .admin__collapsible-title{background:#d0d0d0}.admin__control-collapsible ._dragover-bottom .admin__collapsible-block-wrapper:before,.admin__control-collapsible ._dragover-top .admin__collapsible-block-wrapper:before{background:#008bdb;content:'';display:block;height:3px;left:0;position:absolute;right:0}.admin__control-collapsible ._dragover-top .admin__collapsible-block-wrapper:before{top:-3px}.admin__control-collapsible ._dragover-bottom .admin__collapsible-block-wrapper:before{bottom:-3px}.admin__control-collapsible .admin__collapsible-block-wrapper.fieldset-wrapper{border:0;margin:0;position:relative}.admin__control-collapsible .admin__collapsible-block-wrapper.fieldset-wrapper .fieldset-wrapper-title{background:#f8f8f8;border:2px solid #ccc}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .admin__collapsible-title{font-size:1.4rem;font-weight:400;line-height:1;padding:1.6rem 4rem 1.6rem 3.8rem}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .admin__collapsible-title:before{left:1rem;right:auto;top:1.4rem}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete{background-color:transparent;border-color:transparent;box-shadow:none;padding:0;position:absolute;right:1rem;top:1.4rem}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete:hover{background-color:transparent;border-color:transparent;box-shadow:none}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete:before{content:'\e630';font-size:2rem}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete>span{display:none}.admin__control-collapsible .admin__collapsible-content{background-color:#fff;margin-bottom:1rem}.admin__control-collapsible .admin__collapsible-content>.fieldset-wrapper{border:1px solid #ccc;margin-top:-1px;padding:1rem}.admin__control-collapsible .admin__collapsible-content .admin__fieldset{padding:0}.admin__control-collapsible .admin__collapsible-content .admin__field:last-child{margin-bottom:0}.admin__control-table-wrapper{max-width:100%;overflow-x:auto;overflow-y:hidden}.admin__control-table{width:100%}.admin__control-table thead{background-color:transparent}.admin__control-table tbody td{vertical-align:top}.admin__control-table tfoot th{padding-bottom:1.3rem}.admin__control-table tfoot th.validation{padding-bottom:0;padding-top:0}.admin__control-table tfoot td{border-top:1px solid #fff}.admin__control-table tfoot .admin__control-table-pagination{float:right;padding-bottom:0}.admin__control-table tfoot .action-previous{margin-right:.5rem}.admin__control-table tfoot .action-next{margin-left:.9rem}.admin__control-table tr:last-child td{border-bottom:none}.admin__control-table tr._dragover-top td{box-shadow:inset 0 3px 0 0 #008bdb}.admin__control-table tr._dragover-bottom td{box-shadow:inset 0 -3px 0 0 #008bdb}.admin__control-table tr._dragged td,.admin__control-table tr._dragged th{background:#d0d0d0}.admin__control-table td,.admin__control-table th{background-color:#efefef;border:0;border-bottom:1px solid #fff;padding:1.3rem 1rem 1.3rem 0;text-align:left;vertical-align:top}.admin__control-table td:first-child,.admin__control-table th:first-child{padding-left:1rem}.admin__control-table td>.admin__control-select,.admin__control-table td>.admin__control-text,.admin__control-table th>.admin__control-select,.admin__control-table th>.admin__control-text{width:100%}.admin__control-table td._hidden,.admin__control-table th._hidden{display:none}.admin__control-table td._fit,.admin__control-table th._fit{width:1px}.admin__control-table th{color:#303030;font-size:1.4rem;font-weight:600;vertical-align:bottom}.admin__control-table th._required span:after{color:#eb5202;content:'*'}.admin__control-table .control-table-actions-th{white-space:nowrap}.admin__control-table .control-table-actions-cell{padding-top:1.8rem;text-align:center;width:1%}.admin__control-table .control-table-options-th{text-align:center;width:10rem}.admin__control-table .control-table-options-cell{text-align:center}.admin__control-table .control-table-text{line-height:3.2rem}.admin__control-table .col-draggable{padding-top:2.2rem;width:1%}.admin__control-table .action-delete{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.admin__control-table .action-delete:hover{background-color:transparent;border-color:transparent;box-shadow:none}.admin__control-table .action-delete:before{content:'\e630';font-size:2rem}.admin__control-table .action-delete>span{display:none}.admin__control-table .draggable-handle{padding:0}.admin__control-table._dragged{outline:#007bdb solid 1px}.admin__control-table-action{background-color:#efefef;border-top:1px solid #fff;padding:1.3rem 1rem}.admin__dynamic-rows._dragged{opacity:.95;position:absolute;z-index:999}.admin__dynamic-rows.admin__control-table .admin__control-fields>.admin__field{border:0;padding:0}.admin__dynamic-rows td>.admin__field{border:0;margin:0;padding:0}.admin__control-table-pagination{padding-bottom:1rem}.admin__control-table-pagination .admin__data-grid-pager{float:right}.admin__field-tooltip{display:inline-block;margin-top:.5rem;max-width:45px;overflow:visible;vertical-align:top;width:0}.admin__field-tooltip:hover{position:relative;z-index:500}.admin__field-option .admin__field-tooltip{margin-top:.5rem}.admin__field-tooltip .admin__field-tooltip-action{margin-left:2rem;position:relative;z-index:2;display:inline-block;text-decoration:none}.admin__field-tooltip .admin__field-tooltip-action:before{-webkit-font-smoothing:antialiased;font-size:2.2rem;line-height:1;color:#514943;content:'\e633';font-family:Icons;vertical-align:middle;display:inline-block;font-weight:400;overflow:hidden;speak:none;text-align:center}.admin__field-tooltip .admin__control-text:focus+.admin__field-tooltip-content,.admin__field-tooltip:hover .admin__field-tooltip-content{display:block}.admin__field-tooltip .admin__field-tooltip-content{bottom:3.8rem;display:none;right:-2.3rem}.admin__field-tooltip .admin__field-tooltip-content:after,.admin__field-tooltip .admin__field-tooltip-content:before{border:1.6rem solid transparent;height:0;width:0;border-top-color:#afadac;content:'';display:block;position:absolute;right:2rem;top:100%;z-index:3}.admin__field-tooltip .admin__field-tooltip-content:after{border-top-color:#fffbbb;margin-top:-1px;z-index:4}.abs-admin__field-tooltip-content,.admin__field-tooltip .admin__field-tooltip-content{box-shadow:0 2px 8px 0 rgba(0,0,0,.3);background:#fffbbb;border:1px solid #afadac;border-radius:1px;padding:1.5rem 2.5rem;position:absolute;width:32rem;z-index:1}.admin__field-fallback-reset{font-size:1.25rem;white-space:nowrap;width:30px}.admin__field-fallback-reset>span{margin-left:.5rem;position:relative}.admin__field-fallback-reset:active{-ms-transform:scale(0.98);transform:scale(0.98)}.admin__field-fallback-reset:before{transition:color .1s linear;content:'\e642';font-size:1.3rem;margin-left:.5rem}.admin__field-fallback-reset:hover{cursor:pointer;text-decoration:none}.admin__field-fallback-reset:focus{background:0 0}.abs-field-size-x-small,.abs-field-sizes.admin__field-x-small>.admin__field-control,.admin__field.admin__field-x-small>.admin__field-control,.admin__fieldset>.admin__field.admin__field-x-small>.admin__field-control,[class*=admin__control-grouped]>.admin__field.admin__field-x-small>.admin__field-control{width:8rem}.abs-field-size-small,.abs-field-sizes.admin__field-small>.admin__field-control,.admin__control-grouped-date>.admin__field-date.admin__field>.admin__field-control,.admin__field.admin__field-small>.admin__field-control,.admin__fieldset>.admin__field.admin__field-small>.admin__field-control,[class*=admin__control-grouped]>.admin__field.admin__field-small>.admin__field-control{width:15rem}.abs-field-size-medium,.abs-field-sizes.admin__field-medium>.admin__field-control,.admin__field.admin__field-medium>.admin__field-control,.admin__fieldset>.admin__field.admin__field-medium>.admin__field-control,[class*=admin__control-grouped]>.admin__field.admin__field-medium>.admin__field-control{width:34rem}.abs-field-size-large,.abs-field-sizes.admin__field-large>.admin__field-control,.admin__field.admin__field-large>.admin__field-control,.admin__fieldset>.admin__field.admin__field-large>.admin__field-control,[class*=admin__control-grouped]>.admin__field.admin__field-large>.admin__field-control{width:64rem}.abs-field-no-label,.admin__field-group-additional,.admin__field-no-label,.admin__fieldset>.admin__field.admin__field-no-label>.admin__field-control{margin-left:calc((100%) * .25 + 30px)}.admin__fieldset{border:0;margin:0;min-width:0;padding:0}.admin__fieldset .fieldset-wrapper.admin__fieldset-section>.fieldset-wrapper-title{padding-left:1rem}.admin__fieldset .fieldset-wrapper.admin__fieldset-section>.fieldset-wrapper-title strong{font-size:1.7rem;font-weight:600}.admin__fieldset .fieldset-wrapper.admin__fieldset-section .admin__fieldset-wrapper-content>.admin__fieldset{padding-top:1rem}.admin__fieldset .fieldset-wrapper.admin__fieldset-section:last-child .admin__fieldset-wrapper-content>.admin__fieldset{padding-bottom:0}.admin__fieldset>.admin__field{border:0;margin:0 0 0 -30px;padding:0}.admin__fieldset>.admin__field:after{clear:both;content:'';display:table}.admin__fieldset>.admin__field>.admin__field-control{width:calc((100%) * .5 - 30px);float:left;margin-left:30px}.admin__fieldset>.admin__field>.admin__field-label{width:calc((100%) * .25 - 30px);float:left;margin-left:30px}.admin__fieldset>.admin__field.admin__field-no-label>.admin__field-label{display:none}.admin__fieldset>.admin__field+.admin__field._empty._no-header{margin-top:-3rem}.admin__fieldset-product-websites{position:relative;z-index:300}.admin__fieldset-note{margin-bottom:2rem}.admin__form-field{border:0;margin:0;padding:0}.admin__field-control .admin__control-text,.admin__field-control .admin__control-textarea,.admin__form-field-control .admin__control-text,.admin__form-field-control .admin__control-textarea{width:100%}.admin__field-label{color:#303030;cursor:pointer;margin:0;text-align:right}.admin__field-label+br{display:none}.admin__field:not(.admin__field-option)>.admin__field-label{font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.4rem;font-weight:600;line-height:3.2rem;padding:0;white-space:nowrap}.admin__field:not(.admin__field-option)>.admin__field-label:before{opacity:0;visibility:hidden;content:'.';margin-left:-7px;overflow:hidden}.admin__field:not(.admin__field-option)>.admin__field-label span{display:inline-block;line-height:1.2;vertical-align:middle;white-space:normal}.admin__field:not(.admin__field-option)>.admin__field-label span[data-config-scope]{position:relative}._required>.admin__field-label>span:after,.required>.admin__field-label>span:after{color:#eb5202;content:'*';display:inline-block;font-size:1.6rem;font-weight:500;line-height:1;margin-left:10px;margin-top:.2rem;position:absolute;z-index:1}._disabled>.admin__field-label{color:#999;cursor:default}.admin__field{margin-bottom:0}.admin__field+.admin__field{margin-top:1.5rem}.admin__field:not(.admin__field-option)~.admin__field-option{margin-top:.5rem}.admin__field.admin__field-option~.admin__field-option{margin-top:.9rem}.admin__field~.admin__field-option:last-child{margin-bottom:.8rem}.admin__fieldset>.admin__field{margin-bottom:3rem;position:relative}.admin__field legend.admin__field-label{opacity:0}.admin__field[data-config-scope]:before{color:gray;content:attr(data-config-scope);display:inline-block;font-size:1.2rem;left:calc((100%) * .75 - 30px);line-height:3.2rem;margin-left:60px;position:absolute;width:calc((100%) * .25 - 30px)}.admin__field-control .admin__field[data-config-scope]:nth-child(n+2):before{content:''}.admin__field._error .admin__field-control [class*=admin__addon-]:before,.admin__field._error .admin__field-control [class*=admin__control-] [class*=admin__addon-]:before,.admin__field._error .admin__field-control>[class*=admin__control-]{border-color:#e22626}.admin__field._disabled,.admin__field._disabled:hover{box-shadow:inherit;cursor:inherit;opacity:1;outline:inherit}.admin__field._hidden{display:none}.admin__field-control+.admin__field-control{margin-top:1.5rem}.admin__field-control._with-tooltip>.admin__control-addon,.admin__field-control._with-tooltip>.admin__control-select,.admin__field-control._with-tooltip>.admin__control-text,.admin__field-control._with-tooltip>.admin__control-textarea,.admin__field-control._with-tooltip>.admin__field-option{max-width:calc(100% - 45px - 4px)}.admin__field-control._with-tooltip .admin__field-tooltip{width:auto}.admin__field-control._with-tooltip .admin__field-option{display:inline-block}.admin__field-control._with-reset>.admin__control-addon,.admin__field-control._with-reset>.admin__control-text,.admin__field-control._with-reset>.admin__control-textarea{width:calc(100% - 30px - .5rem - 4px)}.admin__field-control._with-reset .admin__field-fallback-reset{margin-left:.5rem;margin-top:1rem;vertical-align:top}.admin__field-control._with-reset._with-tooltip>.admin__control-addon,.admin__field-control._with-reset._with-tooltip>.admin__control-text,.admin__field-control._with-reset._with-tooltip>.admin__control-textarea{width:calc(100% - 30px - .5rem - 45px - 8px)}.admin__fieldset>.admin__field-collapsible{margin-bottom:0}.admin__fieldset>.admin__field-collapsible .admin__field-control{border-top:1px solid #ccc;display:block;font-size:1.7rem;font-weight:700;padding:1.7rem 0;width:calc(97%)}.admin__fieldset>.admin__field-collapsible .admin__field-option{padding-top:0}.admin__field-collapsible+div{margin-top:2.5rem}.admin__field-collapsible .admin__control-radio+label:before{height:1.8rem;width:1.8rem}.admin__field-collapsible .admin__control-radio:checked+label:after{left:4px;top:5px}.admin__field-error{background:#fffbbb;border:1px solid #ee7d7d;box-sizing:border-box;color:#555;display:block;font-size:1.2rem;font-weight:400;line-height:1.2;margin:.2rem 0 0;padding:.8rem 1rem .9rem}.admin__field-note{color:#303030;font-size:1.2rem;margin:10px 0 0;padding:0}.admin__additional-info{padding-top:1rem}.admin__field-option{padding-top:.7rem}.admin__field-option .admin__field-label{text-align:left}.admin__field-control>.admin__field-option:nth-child(1):nth-last-child(2),.admin__field-control>.admin__field-option:nth-child(2):nth-last-child(1){display:inline-block}.admin__field-control>.admin__field-option:nth-child(1):nth-last-child(2)+.admin__field-option,.admin__field-control>.admin__field-option:nth-child(2):nth-last-child(1)+.admin__field-option{display:inline-block;margin-left:41px;margin-top:0}.admin__field-control>.admin__field-option:nth-child(1):nth-last-child(2)+.admin__field-option:before,.admin__field-control>.admin__field-option:nth-child(2):nth-last-child(1)+.admin__field-option:before{background:#cacaca;content:'';display:inline-block;height:20px;margin-left:-20px;position:absolute;width:1px}.admin__field-value{display:inline-block;padding-top:.7rem}.admin__field-service{padding-top:1rem}.admin__control-fields>.admin__field:first-child,[class*=admin__control-grouped]>.admin__field:first-child{position:static}.admin__control-fields>.admin__field:first-child>.admin__field-label,[class*=admin__control-grouped]>.admin__field:first-child>.admin__field-label{width:calc((100%) * .25 - 30px);float:left;margin-left:30px;background:#fff;cursor:pointer;left:0;position:absolute;top:0}.admin__control-fields>.admin__field:first-child>.admin__field-label span:before,[class*=admin__control-grouped]>.admin__field:first-child>.admin__field-label span:before{display:block}.admin__control-fields>.admin__field._disabled>.admin__field-label,[class*=admin__control-grouped]>.admin__field._disabled>.admin__field-label{cursor:default}.admin__control-fields>.admin__field>.admin__field-label span:before,[class*=admin__control-grouped]>.admin__field>.admin__field-label span:before{display:none}.admin__control-fields .admin__field-label~.admin__field-control{width:100%}.admin__control-fields .admin__field-option{padding-top:0}[class*=admin__control-grouped]{box-sizing:border-box;display:table;width:100%}[class*=admin__control-grouped]>.admin__field{display:table-cell;vertical-align:top}[class*=admin__control-grouped]>.admin__field>.admin__field-control{float:none;width:100%}[class*=admin__control-grouped]>.admin__field.admin__field-default,[class*=admin__control-grouped]>.admin__field.admin__field-large,[class*=admin__control-grouped]>.admin__field.admin__field-medium,[class*=admin__control-grouped]>.admin__field.admin__field-small,[class*=admin__control-grouped]>.admin__field.admin__field-x-small{width:1px}[class*=admin__control-grouped]>.admin__field.admin__field-default+.admin__field:last-child,[class*=admin__control-grouped]>.admin__field.admin__field-large+.admin__field:last-child,[class*=admin__control-grouped]>.admin__field.admin__field-medium+.admin__field:last-child,[class*=admin__control-grouped]>.admin__field.admin__field-small+.admin__field:last-child,[class*=admin__control-grouped]>.admin__field.admin__field-x-small+.admin__field:last-child{width:auto}[class*=admin__control-grouped]>.admin__field:nth-child(n+2){padding-left:20px}.admin__control-group-equal{table-layout:fixed}.admin__control-group-equal>.admin__field{width:50%}.admin__field-control-group{margin-top:.8rem}.admin__field-control-group>.admin__field{padding:0}.admin__control-grouped-date>.admin__field-date{white-space:nowrap;width:1px}.admin__control-grouped-date>.admin__field-date.admin__field>.admin__field-control{float:left;position:relative}.admin__control-grouped-date>.admin__field-date+.admin__field:last-child{width:auto}.admin__control-grouped-date>.admin__field-date+.admin__field-date>.admin__field-label{float:left;padding-right:20px}.admin__control-grouped-date .ui-datepicker-trigger{left:100%;top:0}.admin__field-group-columns.admin__field-control.admin__control-grouped{width:calc((100%) * 1 - 30px);float:left;margin-left:30px}.admin__field-group-columns>.admin__field:first-child>.admin__field-label{float:none;margin:0;opacity:1;position:static;text-align:left}.admin__field-group-columns .admin__control-select{width:100%}.admin__field-group-additional{clear:both}.admin__field-group-additional .action-advanced{margin-top:1rem}.admin__field-group-additional .action-secondary{width:100%}.admin__field-group-show-label{white-space:nowrap}.admin__field-group-show-label>.admin__field-control,.admin__field-group-show-label>.admin__field-label{display:inline-block;vertical-align:top}.admin__field-group-show-label>.admin__field-label{margin-right:20px}.admin__field-complex{margin:1rem 0 3rem;padding-left:1rem}.admin__field:not(._hidden)+.admin__field-complex{margin-top:3rem}.admin__field-complex .admin__field-complex-title{clear:both;color:#303030;font-size:1.7rem;font-weight:600;letter-spacing:.025em;margin-bottom:1rem}.admin__field-complex .admin__field-complex-elements{float:right;max-width:40%}.admin__field-complex .admin__field-complex-elements button{margin-left:1rem}.admin__field-complex .admin__field-complex-content{max-width:60%;overflow:hidden}.admin__field-complex .admin__field-complex-text{margin-left:-1rem}.admin__field-complex+.admin__field._empty._no-header{margin-top:-3rem}.admin__legend{float:left;position:static;width:100%}.admin__legend+br{clear:left;display:block;height:0;overflow:hidden}.message{margin-bottom:3rem}.message-icon-top:before{margin-top:0;top:1.8rem}.nav{background-color:#f8f8f8;border-bottom:1px solid #e3e3e3;border-top:1px solid #e3e3e3;display:none;margin-bottom:3rem;padding:2.2rem 1.5rem 0 0}.nav .btn-group,.nav-bar-outer-actions{float:right;margin-bottom:1.7rem}.nav .btn-group .btn-wrap,.nav-bar-outer-actions .btn-wrap{float:right;margin-left:.5rem;margin-right:.5rem}.nav .btn-group .btn-wrap .btn,.nav-bar-outer-actions .btn-wrap .btn{padding-left:.5rem;padding-right:.5rem}.nav-bar-outer-actions{margin-top:-10.6rem;padding-right:1.5rem}.btn-wrap-try-again{width:9.5rem}.btn-wrap-next,.btn-wrap-prev{width:8.5rem}.nav-bar{counter-reset:i;float:left;margin:0 1rem 1.7rem 0;padding:0;position:relative;white-space:nowrap}.nav-bar:before{background-color:#d4d4d4;background-repeat:repeat-x;background-image:linear-gradient(to bottom,#d1d1d1 0,#d4d4d4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#d1d1d1', endColorstr='#d4d4d4', GradientType=0);border-bottom:1px solid #d9d9d9;border-top:1px solid #bfbfbf;content:'';height:1rem;left:5.15rem;position:absolute;right:5.15rem;top:.7rem}.nav-bar>li{display:inline-block;font-size:0;position:relative;vertical-align:top;width:10.3rem}.nav-bar>li:first-child:after{display:none}.nav-bar>li:after{background-color:#514943;content:'';height:.5rem;left:calc(-50% + .25rem);position:absolute;right:calc(50% + .7rem);top:.9rem}.nav-bar>li.disabled:before,.nav-bar>li.ui-state-disabled:before{bottom:0;content:'';left:0;position:absolute;right:0;top:0;z-index:1}.nav-bar>li.active~li:after,.nav-bar>li.ui-state-active~li:after{display:none}.nav-bar>li.active~li a:after,.nav-bar>li.ui-state-active~li a:after{background-color:transparent;border-color:transparent;color:#a6a6a6}.nav-bar>li.active a,.nav-bar>li.ui-state-active a{color:#000}.nav-bar>li.active a:hover,.nav-bar>li.ui-state-active a:hover{cursor:default}.nav-bar>li.active a:after,.nav-bar>li.ui-state-active a:after{background-color:#fff;content:''}.nav-bar a{color:#514943;display:block;font-size:1.2rem;font-weight:600;line-height:1.2;overflow:hidden;padding:3rem .5em 0;position:relative;text-align:center;text-overflow:ellipsis}.nav-bar a:hover{text-decoration:none}.nav-bar a:after{background-color:#514943;border:.4rem solid #514943;border-radius:100%;color:#fff;content:counter(i);counter-increment:i;height:1.5rem;left:50%;line-height:.6;margin-left:-.8rem;position:absolute;right:auto;text-align:center;top:.4rem;width:1.5rem}.nav-bar a:before{background-color:#d6d6d6;border:1px solid transparent;border-bottom-color:#d9d9d9;border-radius:100%;border-top-color:#bfbfbf;content:'';height:2.3rem;left:50%;line-height:1;margin-left:-1.2rem;position:absolute;top:0;width:2.3rem}.tooltip{display:block;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.19rem;font-weight:400;line-height:1.4;opacity:0;position:absolute;visibility:visible;z-index:10}.tooltip.in{opacity:.9}.tooltip.top{margin-top:-4px;padding:8px 0}.tooltip.right{margin-left:4px;padding:0 8px}.tooltip.bottom{margin-top:4px;padding:8px 0}.tooltip.left{margin-left:-4px;padding:0 8px}.tooltip p:last-child{margin-bottom:0}.tooltip-inner{background-color:#fff;border:1px solid #adadad;border-radius:0;box-shadow:1px 1px 1px #ccc;color:#41362f;max-width:31rem;padding:.5em 1em;text-decoration:none}.tooltip-arrow,.tooltip-arrow:after{border:solid transparent;height:0;position:absolute;width:0}.tooltip-arrow:after{content:'';position:absolute}.tooltip.top .tooltip-arrow,.tooltip.top .tooltip-arrow:after{border-top-color:#949494;border-width:8px 8px 0;bottom:0;left:50%;margin-left:-8px}.tooltip.top-left .tooltip-arrow,.tooltip.top-left .tooltip-arrow:after{border-top-color:#949494;border-width:8px 8px 0;bottom:0;margin-bottom:-8px;right:8px}.tooltip.top-right .tooltip-arrow,.tooltip.top-right .tooltip-arrow:after{border-top-color:#949494;border-width:8px 8px 0;bottom:0;left:8px;margin-bottom:-8px}.tooltip.right .tooltip-arrow,.tooltip.right .tooltip-arrow:after{border-right-color:#949494;border-width:8px 8px 8px 0;left:1px;margin-top:-8px;top:50%}.tooltip.right .tooltip-arrow:after{border-right-color:#fff;border-width:6px 7px 6px 0;margin-left:0;margin-top:-6px}.tooltip.left .tooltip-arrow,.tooltip.left .tooltip-arrow:after{border-left-color:#949494;border-width:8px 0 8px 8px;margin-top:-8px;right:0;top:50%}.tooltip.bottom .tooltip-arrow,.tooltip.bottom .tooltip-arrow:after{border-bottom-color:#949494;border-width:0 8px 8px;left:50%;margin-left:-8px;top:0}.tooltip.bottom-left .tooltip-arrow,.tooltip.bottom-left .tooltip-arrow:after{border-bottom-color:#949494;border-width:0 8px 8px;margin-top:-8px;right:8px;top:0}.tooltip.bottom-right .tooltip-arrow,.tooltip.bottom-right .tooltip-arrow:after{border-bottom-color:#949494;border-width:0 8px 8px;left:8px;margin-top:-8px;top:0}.password-strength{display:block;margin:0 -.3rem 1em;white-space:nowrap}.password-strength.password-strength-too-short .password-strength-item:first-child,.password-strength.password-strength-weak .password-strength-item:first-child,.password-strength.password-strength-weak .password-strength-item:first-child+.password-strength-item{background-color:#e22626}.password-strength.password-strength-fair .password-strength-item:first-child,.password-strength.password-strength-fair .password-strength-item:first-child+.password-strength-item,.password-strength.password-strength-fair .password-strength-item:first-child+.password-strength-item+.password-strength-item{background-color:#ef672f}.password-strength.password-strength-good .password-strength-item:first-child,.password-strength.password-strength-good .password-strength-item:first-child+.password-strength-item,.password-strength.password-strength-good .password-strength-item:first-child+.password-strength-item+.password-strength-item,.password-strength.password-strength-good .password-strength-item:first-child+.password-strength-item+.password-strength-item+.password-strength-item,.password-strength.password-strength-strong .password-strength-item{background-color:#79a22e}.password-strength .password-strength-item{background-color:#ccc;display:inline-block;font-size:0;height:1.4rem;margin-right:.3rem;width:calc(20% - .6rem)}@keyframes progress-bar-stripes{from{background-position:4rem 0}to{background-position:0 0}}.progress{background-color:#fafafa;border:1px solid #ccc;clear:left;height:3rem;margin-bottom:3rem;overflow:hidden}.progress-bar{background-color:#79a22e;color:#fff;float:left;font-size:1.19rem;height:100%;line-height:3rem;text-align:center;transition:width .6s ease;width:0}.progress-bar.active{animation:progress-bar-stripes 2s linear infinite}.progress-bar-text-description{margin-bottom:1.6rem}.progress-bar-text-progress{text-align:right}.page-columns .page-inner-sidebar{margin:0 0 3rem}.page-header{margin-bottom:2.7rem;padding-bottom:2rem;position:relative}.page-header:before{border-bottom:1px solid #e3e3e3;bottom:0;content:'';display:block;height:1px;left:3rem;position:absolute;right:3rem}.container .page-header:before{content:normal}.page-header .message{margin-bottom:1.8rem}.page-header .message+.message{margin-top:-1.5rem}.page-header .admin__action-dropdown,.page-header .search-global-input{transition:none}.container .page-header{margin-bottom:0}.page-title-wrapper{margin-top:1.1rem}.container .page-title-wrapper{background:url(../../pub/images/logo.svg) no-repeat;min-height:41px;padding:4px 0 0 45px}.admin__menu .level-0:first-child>a{margin-top:1.6rem}.admin__menu .level-0:first-child>a:after{top:-1.6rem}.admin__menu .level-0:first-child._active>a:after{display:block}.admin__menu .level-0>a{padding-bottom:1.3rem;padding-top:1.3rem}.admin__menu .level-0>a:before{margin-bottom:.7rem}.admin__menu .item-home>a:before{content:'\e611';font-size:2.3rem;padding-top:-.1rem}.admin__menu .item-component>a:before{content:'\e612'}.admin__menu .item-extension>a:before{content:'\e647'}.admin__menu .item-upgrade>a:before{content:'\e614'}.admin__menu .item-system-config>a:before{content:'\e610'}.admin__menu .item-tools>a:before{content:'\e613'}.modal-sub-title{font-size:1.7rem;font-weight:600}.modal-connect-signin .modal-inner-wrap{max-width:80rem}@keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}.ngdialog{-webkit-overflow-scrolling:touch;bottom:0;box-sizing:border-box;left:0;overflow:auto;position:fixed;right:0;top:0;z-index:999}.ngdialog *,.ngdialog:after,.ngdialog:before{box-sizing:inherit}.ngdialog.ngdialog-disabled-animation *{animation:none!important}.ngdialog.ngdialog-closing .ngdialog-content,.ngdialog.ngdialog-closing .ngdialog-overlay{-webkit-animation:ngdialog-fadeout .5s;-webkit-backface-visibility:hidden;animation:ngdialog-fadeout .5s}.ngdialog-overlay{-webkit-animation:ngdialog-fadein .5s;-webkit-backface-visibility:hidden;animation:ngdialog-fadein .5s;background:rgba(0,0,0,.4);bottom:0;left:0;position:fixed;right:0;top:0}.ngdialog-content{-webkit-animation:ngdialog-fadein .5s;-webkit-backface-visibility:hidden;animation:ngdialog-fadein .5s}body.ngdialog-open{overflow:hidden}.component-indicator{border-radius:50%;cursor:help;display:inline-block;height:16px;text-align:center;vertical-align:middle;width:16px}.component-indicator::after,.component-indicator::before{background:#fff;display:block;opacity:0;position:absolute;transition:opacity .2s linear .1s;visibility:hidden}.component-indicator::before{border:1px solid #adadad;border-radius:1px;box-shadow:0 0 2px rgba(0,0,0,.4);content:attr(data-label);font-size:1.2rem;margin:30px 0 0 -10px;min-width:50px;padding:4px 5px}.component-indicator::after{border-color:#999;border-style:solid;border-width:1px 0 0 1px;box-shadow:-1px -1px 1px rgba(0,0,0,.1);content:'';height:10px;margin:9px 0 0 5px;-ms-transform:rotate(45deg);transform:rotate(45deg);width:10px}.component-indicator:hover::after,.component-indicator:hover::before{opacity:1;transition:opacity .2s linear;visibility:visible}.component-indicator span{display:block;height:16px;overflow:hidden;width:16px}.component-indicator span:before{content:'';display:block;font-family:Icons;font-size:16px;height:100%;line-height:16px;width:100%}.component-indicator._on{background:#79a22e}.component-indicator._off{background:#e22626}.component-indicator._off span:before{background:#fff;height:4px;margin:8px auto 20px;width:12px}.component-indicator._info{background:0 0}.component-indicator._info span{width:21px}.component-indicator._info span:before{color:#008bdb;content:'\e648';font-family:Icons;font-size:16px}.component-indicator._tooltip{background:0 0;margin:0 0 8px 5px}.component-indicator._tooltip a{width:21px}.component-indicator._tooltip a:hover{text-decoration:none}.component-indicator._tooltip a:before{color:#514943;content:'\e633';font-family:Icons;font-size:16px}.col-manager-item-name .data-grid-data{padding-left:5px}.col-manager-item-name .ng-hide+.data-grid-data{padding-left:24px}.col-manager-item-name ._hide-dependencies,.col-manager-item-name ._show-dependencies{cursor:pointer;padding-left:24px;position:relative}.col-manager-item-name ._hide-dependencies:before,.col-manager-item-name ._show-dependencies:before{display:block;font-family:Icons;font-size:12px;left:0;position:absolute;top:1px}.col-manager-item-name ._show-dependencies:before{content:'\e62b'}.col-manager-item-name ._hide-dependencies:before{content:'\e628'}.col-manager-item-name ._no-dependencies{padding-left:24px}.product-modules-block{font-size:1.2rem;padding:15px 0 0}.col-manager-item-name .product-modules-block{padding-left:1rem}.product-modules-descriprion,.product-modules-title{font-weight:700;margin:0 0 7px}.product-modules-list{font-size:1.1rem;list-style:none;margin:0}.col-manager-item-name .product-modules-list{margin-left:15px}.col-manager-item-name .product-modules-list li{padding:0 0 0 15px;position:relative}.product-modules-list li{margin:0 0 .5rem}.product-modules-list .component-indicator{height:10px;left:0;position:absolute;top:3px;width:10px}.module-summary{white-space:nowrap}.module-summary-title{font-size:2.1rem;margin-right:1rem}.app-updater .nav{display:block;margin-bottom:3.1rem;margin-top:-2.8rem}.app-updater .nav-bar-outer-actions{margin-top:1rem;padding-right:0}.app-updater .nav-bar-outer-actions .btn-wrap-cancel{margin-right:2.6rem}.main{padding-bottom:2rem;padding-top:3rem}.menu-wrapper .logo-static{pointer-events:none}.header{display:none}.header .logo{float:left;height:4.1rem;width:3.5rem}.header-title{font-size:2.8rem;letter-spacing:.02em;line-height:1.4;margin:2.5rem 0 3.5rem 5rem}.page-title{margin-bottom:1rem}.page-sub-title{font-size:2rem}.accent-box{margin-bottom:2rem}.accent-box .btn-prime{margin-top:1.5rem}.spinner.side{float:left;font-size:2.4rem;margin-left:2rem;margin-top:-5px}.page-landing{margin:7.6% auto 0;max-width:44rem;text-align:center}.page-landing .logo{height:5.6rem;margin-bottom:2rem;width:19.2rem}.page-landing .text-version{margin-bottom:3rem}.page-landing .text-welcome{margin-bottom:6.5rem}.page-landing .text-terms{margin-bottom:2.5rem;text-align:center}.page-landing .btn-submit,.page-license .license-text{margin-bottom:2rem}.page-license .page-license-footer{text-align:right}.readiness-check-item{margin-bottom:4rem;min-height:2.5rem}.readiness-check-item .spinner{float:left;font-size:2.5rem;margin:-.4rem 0 0 1.7rem}.readiness-check-title{font-size:1.4rem;font-weight:700;margin-bottom:.1rem;margin-left:5.7rem}.readiness-check-content{margin-left:5.7rem;margin-right:22rem;position:relative}.readiness-check-content .readiness-check-title{margin-left:0}.readiness-check-content .list{margin-top:-.3rem}.readiness-check-side{left:100%;padding-left:2.4rem;position:absolute;top:0;width:22rem}.readiness-check-side .side-title{margin-bottom:0}.readiness-check-icon{float:left;margin-left:1.7rem;margin-top:.3rem}.extensions-information{margin-bottom:5rem}.extensions-information h3{font-size:1.4rem;margin-bottom:1.3rem}.extensions-information .message{margin-bottom:2.5rem}.extensions-information .message:before{margin-top:0;top:1.8rem}.extensions-information .extensions-container{padding:0 2rem}.extensions-information .list{margin-bottom:1rem}.extensions-information .list select{cursor:pointer}.extensions-information .list select:disabled{background:#ccc;cursor:default}.extensions-information .list .extension-delete{font-size:1.7rem;padding-top:0}.delete-modal-wrap{padding:0 4% 4rem}.delete-modal-wrap h3{font-size:3.4rem;display:inline-block;font-weight:300;margin:0 0 2rem;padding:.9rem 0 0;vertical-align:top}.delete-modal-wrap .actions{padding:3rem 0 0}.page-web-configuration .form-el-insider-wrap{width:auto}.page-web-configuration .form-el-insider{width:15.4rem}.page-web-configuration .form-el-insider-input .form-el-input{width:16.5rem}.customize-your-store .advanced-modules-count,.customize-your-store .advanced-modules-select{padding-left:1.5rem}.customize-your-store .customize-your-store-advanced{min-width:0}.customize-your-store .message-error:before{margin-top:0;top:1.8rem}.customize-your-store .message-error a{color:#333;text-decoration:underline}.customize-your-store .message-error .form-label:before{background:#fff}.customize-your-store .customize-database-clean p{margin-top:2.5rem}.content-install{margin-bottom:2rem}.console{border:1px solid #ccc;font-family:'Courier New',Courier,monospace;font-weight:300;height:20rem;margin:1rem 0 2rem;overflow-y:auto;padding:1.5rem 2rem 2rem;resize:vertical}.console .text-danger{color:#e22626}.console .text-success{color:#090}.console .hidden{display:none}.content-success .btn-prime{margin-top:1.5rem}.jumbo-title{font-size:3.6rem}.jumbo-title .jumbo-icon{font-size:3.8rem;margin-right:.25em;position:relative;top:.15em}.install-database-clean{margin-top:4rem}.install-database-clean .btn{margin-right:1rem}.page-sub-title{margin-bottom:2.1rem;margin-top:3rem}.multiselect-custom{max-width:71.1rem}.content-install{margin-top:3.7rem}.home-page-inner-wrap{margin:0 auto;max-width:91rem}.setup-home-title{margin-bottom:3.9rem;padding-top:1.8rem;text-align:center}.setup-home-item{background-color:#fafafa;border:1px solid #ccc;color:#333;display:block;margin-bottom:2rem;margin-left:1.3rem;margin-right:1.3rem;min-height:30rem;padding:2rem;text-align:center}.setup-home-item:hover{border-color:#8c8c8c;color:#333;text-decoration:none;transition:border-color .1s linear}.setup-home-item:active{-ms-transform:scale(0.99);transform:scale(0.99)}.setup-home-item:before{display:block;font-size:7rem;margin-bottom:3.3rem;margin-top:4rem}.setup-home-item-component:before,.setup-home-item-extension:before{content:'\e612'}.setup-home-item-module:before{content:'\e647'}.setup-home-item-upgrade:before{content:'\e614'}.setup-home-item-configuration:before{content:'\e610'}.setup-home-item-title{display:block;font-size:1.8rem;letter-spacing:.025em;margin-bottom:1rem}.setup-home-item-description{display:block}.extension-manager-wrap{border:1px solid #bbb;margin:0 0 4rem}.extension-manager-account{font-size:2.1rem;display:inline-block;font-weight:400}.extension-manager-title{font-size:3.2rem;background-color:#f8f8f8;border-bottom:1px solid #e3e3e3;color:#41362f;font-weight:600;line-height:1.2;padding:2rem}.extension-manager-content{padding:2.5rem 2rem 2rem}.extension-manager-items{list-style:none;margin:0;text-align:center}.extension-manager-items .btn{border:1px solid #adadad;display:block;margin:1rem auto 0}.extension-manager-items .item-title{font-size:2.1rem;display:inline-block;text-align:left}.extension-manager-items .item-number{font-size:4.1rem;display:inline-block;line-height:.8;margin:0 5px 1.5rem 0;vertical-align:top}.extension-manager-items .item-date{font-size:2.6rem;margin-top:1px}.extension-manager-items .item-date-title{font-size:1.5rem}.extension-manager-items .item-install{margin:0 0 2rem}.sync-login-wrap{padding:0 10% 4rem}.sync-login-wrap .legend{font-size:2.6rem;color:#eb5202;float:left;font-weight:300;line-height:1.2;margin:-1rem 0 2.5rem;position:static;width:100%}.sync-login-wrap .legend._hidden{display:none}.sync-login-wrap .login-header{font-size:3.4rem;font-weight:300;margin:0 0 2rem}.sync-login-wrap .login-header span{display:inline-block;padding:.9rem 0 0;vertical-align:top}.sync-login-wrap h4{font-size:1.4rem;margin:0 0 2rem}.sync-login-wrap .sync-login-steps{margin:0 0 2rem 1.5rem}.sync-login-wrap .sync-login-steps li{padding:0 0 0 1rem}.sync-login-wrap .form-row .form-label{display:inline-block}.sync-login-wrap .form-row .form-label.required{padding-left:1.5rem}.sync-login-wrap .form-row .form-label.required:after{left:0;position:absolute;right:auto}.sync-login-wrap .form-row{max-width:28rem}.sync-login-wrap .form-actions{display:table;margin-top:-1.3rem}.sync-login-wrap .form-actions .links{display:table-header-group}.sync-login-wrap .form-actions .actions{padding:3rem 0 0}@media all and (max-width:1047px){.admin__menu .submenu li{min-width:19.8rem}.nav{padding-bottom:5.38rem;padding-left:1.5rem;text-align:center}.nav-bar{display:inline-block;float:none;margin-right:0;vertical-align:top}.nav .btn-group,.nav-bar-outer-actions{display:inline-block;float:none;margin-top:-8.48rem;text-align:center;vertical-align:top;width:100%}.nav-bar-outer-actions{padding-right:0}.nav-bar-outer-actions .outer-actions-inner-wrap{display:inline-block}.app-updater .nav{padding-bottom:1.7rem}.app-updater .nav-bar-outer-actions{margin-top:2rem}}@media all and (min-width:768px){.page-layout-admin-2columns-left .page-columns{margin-left:-30px}.page-layout-admin-2columns-left .page-columns:after{clear:both;content:'';display:table}.page-layout-admin-2columns-left .page-columns .main-col{width:calc((100%) * .75 - 30px);float:right}.page-layout-admin-2columns-left .page-columns .side-col{width:calc((100%) * .25 - 30px);float:left;margin-left:30px}.col-m-1,.col-m-10,.col-m-11,.col-m-12,.col-m-2,.col-m-3,.col-m-4,.col-m-5,.col-m-6,.col-m-7,.col-m-8,.col-m-9{float:left}.col-m-12{width:100%}.col-m-11{width:91.66666667%}.col-m-10{width:83.33333333%}.col-m-9{width:75%}.col-m-8{width:66.66666667%}.col-m-7{width:58.33333333%}.col-m-6{width:50%}.col-m-5{width:41.66666667%}.col-m-4{width:33.33333333%}.col-m-3{width:25%}.col-m-2{width:16.66666667%}.col-m-1{width:8.33333333%}.col-m-pull-12{right:100%}.col-m-pull-11{right:91.66666667%}.col-m-pull-10{right:83.33333333%}.col-m-pull-9{right:75%}.col-m-pull-8{right:66.66666667%}.col-m-pull-7{right:58.33333333%}.col-m-pull-6{right:50%}.col-m-pull-5{right:41.66666667%}.col-m-pull-4{right:33.33333333%}.col-m-pull-3{right:25%}.col-m-pull-2{right:16.66666667%}.col-m-pull-1{right:8.33333333%}.col-m-pull-0{right:auto}.col-m-push-12{left:100%}.col-m-push-11{left:91.66666667%}.col-m-push-10{left:83.33333333%}.col-m-push-9{left:75%}.col-m-push-8{left:66.66666667%}.col-m-push-7{left:58.33333333%}.col-m-push-6{left:50%}.col-m-push-5{left:41.66666667%}.col-m-push-4{left:33.33333333%}.col-m-push-3{left:25%}.col-m-push-2{left:16.66666667%}.col-m-push-1{left:8.33333333%}.col-m-push-0{left:auto}.col-m-offset-12{margin-left:100%}.col-m-offset-11{margin-left:91.66666667%}.col-m-offset-10{margin-left:83.33333333%}.col-m-offset-9{margin-left:75%}.col-m-offset-8{margin-left:66.66666667%}.col-m-offset-7{margin-left:58.33333333%}.col-m-offset-6{margin-left:50%}.col-m-offset-5{margin-left:41.66666667%}.col-m-offset-4{margin-left:33.33333333%}.col-m-offset-3{margin-left:25%}.col-m-offset-2{margin-left:16.66666667%}.col-m-offset-1{margin-left:8.33333333%}.col-m-offset-0{margin-left:0}.page-columns{margin-left:-30px}.page-columns:after{clear:both;content:'';display:table}.page-columns .page-inner-content{width:calc((100%) * .75 - 30px);float:right}.page-columns .page-inner-sidebar{width:calc((100%) * .25 - 30px);float:left;margin-left:30px}}@media all and (min-width:1048px){.col-l-1,.col-l-10,.col-l-11,.col-l-12,.col-l-2,.col-l-3,.col-l-4,.col-l-5,.col-l-6,.col-l-7,.col-l-8,.col-l-9{float:left}.col-l-12{width:100%}.col-l-11{width:91.66666667%}.col-l-10{width:83.33333333%}.col-l-9{width:75%}.col-l-8{width:66.66666667%}.col-l-7{width:58.33333333%}.col-l-6{width:50%}.col-l-5{width:41.66666667%}.col-l-4{width:33.33333333%}.col-l-3{width:25%}.col-l-2{width:16.66666667%}.col-l-1{width:8.33333333%}.col-l-pull-12{right:100%}.col-l-pull-11{right:91.66666667%}.col-l-pull-10{right:83.33333333%}.col-l-pull-9{right:75%}.col-l-pull-8{right:66.66666667%}.col-l-pull-7{right:58.33333333%}.col-l-pull-6{right:50%}.col-l-pull-5{right:41.66666667%}.col-l-pull-4{right:33.33333333%}.col-l-pull-3{right:25%}.col-l-pull-2{right:16.66666667%}.col-l-pull-1{right:8.33333333%}.col-l-pull-0{right:auto}.col-l-push-12{left:100%}.col-l-push-11{left:91.66666667%}.col-l-push-10{left:83.33333333%}.col-l-push-9{left:75%}.col-l-push-8{left:66.66666667%}.col-l-push-7{left:58.33333333%}.col-l-push-6{left:50%}.col-l-push-5{left:41.66666667%}.col-l-push-4{left:33.33333333%}.col-l-push-3{left:25%}.col-l-push-2{left:16.66666667%}.col-l-push-1{left:8.33333333%}.col-l-push-0{left:auto}.col-l-offset-12{margin-left:100%}.col-l-offset-11{margin-left:91.66666667%}.col-l-offset-10{margin-left:83.33333333%}.col-l-offset-9{margin-left:75%}.col-l-offset-8{margin-left:66.66666667%}.col-l-offset-7{margin-left:58.33333333%}.col-l-offset-6{margin-left:50%}.col-l-offset-5{margin-left:41.66666667%}.col-l-offset-4{margin-left:33.33333333%}.col-l-offset-3{margin-left:25%}.col-l-offset-2{margin-left:16.66666667%}.col-l-offset-1{margin-left:8.33333333%}.col-l-offset-0{margin-left:0}}@media all and (min-width:1440px){.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9{float:left}.col-xl-12{width:100%}.col-xl-11{width:91.66666667%}.col-xl-10{width:83.33333333%}.col-xl-9{width:75%}.col-xl-8{width:66.66666667%}.col-xl-7{width:58.33333333%}.col-xl-6{width:50%}.col-xl-5{width:41.66666667%}.col-xl-4{width:33.33333333%}.col-xl-3{width:25%}.col-xl-2{width:16.66666667%}.col-xl-1{width:8.33333333%}.col-xl-pull-12{right:100%}.col-xl-pull-11{right:91.66666667%}.col-xl-pull-10{right:83.33333333%}.col-xl-pull-9{right:75%}.col-xl-pull-8{right:66.66666667%}.col-xl-pull-7{right:58.33333333%}.col-xl-pull-6{right:50%}.col-xl-pull-5{right:41.66666667%}.col-xl-pull-4{right:33.33333333%}.col-xl-pull-3{right:25%}.col-xl-pull-2{right:16.66666667%}.col-xl-pull-1{right:8.33333333%}.col-xl-pull-0{right:auto}.col-xl-push-12{left:100%}.col-xl-push-11{left:91.66666667%}.col-xl-push-10{left:83.33333333%}.col-xl-push-9{left:75%}.col-xl-push-8{left:66.66666667%}.col-xl-push-7{left:58.33333333%}.col-xl-push-6{left:50%}.col-xl-push-5{left:41.66666667%}.col-xl-push-4{left:33.33333333%}.col-xl-push-3{left:25%}.col-xl-push-2{left:16.66666667%}.col-xl-push-1{left:8.33333333%}.col-xl-push-0{left:auto}.col-xl-offset-12{margin-left:100%}.col-xl-offset-11{margin-left:91.66666667%}.col-xl-offset-10{margin-left:83.33333333%}.col-xl-offset-9{margin-left:75%}.col-xl-offset-8{margin-left:66.66666667%}.col-xl-offset-7{margin-left:58.33333333%}.col-xl-offset-6{margin-left:50%}.col-xl-offset-5{margin-left:41.66666667%}.col-xl-offset-4{margin-left:33.33333333%}.col-xl-offset-3{margin-left:25%}.col-xl-offset-2{margin-left:16.66666667%}.col-xl-offset-1{margin-left:8.33333333%}.col-xl-offset-0{margin-left:0}}@media all and (max-width:767px){.abs-clearer-mobile:after,.nav-bar:after{clear:both;content:'';display:table}.list-definition>dt{float:none}.list-definition>dd{margin-left:0}.form-row .form-label{text-align:left}.form-row .form-label.required:after{position:static}.nav{padding-bottom:0;padding-left:0;padding-right:0}.nav-bar-outer-actions{margin-top:0}.nav-bar{display:block;margin-bottom:0;margin-left:auto;margin-right:auto;width:30.9rem}.nav-bar:before{display:none}.nav-bar>li{float:left;min-height:9rem}.nav-bar>li:after{display:none}.nav-bar>li:nth-child(4n){clear:both}.nav-bar a{line-height:1.4}.tooltip{display:none!important}.readiness-check-content{margin-right:2rem}.readiness-check-side{padding:2rem 0;position:static}.form-el-insider,.form-el-insider-wrap,.page-web-configuration .form-el-insider-input,.page-web-configuration .form-el-insider-input .form-el-input{display:block;width:100%}}@media all and (max-width:479px){.nav-bar{width:23.175rem}.nav-bar>li{width:7.725rem}.nav .btn-group .btn-wrap-try-again,.nav-bar-outer-actions .btn-wrap-try-again{clear:both;display:block;float:none;margin-left:auto;margin-right:auto;margin-top:1rem;padding-top:1rem}} \ No newline at end of file +.abs-action-delete,.abs-icon,.action-close:before,.action-next:before,.action-previous:before,.admin-user .admin__action-dropdown:before,.admin__action-multiselect-dropdown:before,.admin__action-multiselect-search-label:before,.admin__control-checkbox+label:before,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete:before,.admin__control-table .action-delete:before,.admin__current-filters-list .action-remove:before,.admin__data-grid-action-bookmarks .action-delete:before,.admin__data-grid-action-bookmarks .action-edit:before,.admin__data-grid-action-bookmarks .action-submit:before,.admin__data-grid-action-bookmarks .admin__action-dropdown:before,.admin__data-grid-action-columns .admin__action-dropdown:before,.admin__data-grid-action-export .admin__action-dropdown:before,.admin__field-fallback-reset:before,.admin__menu .level-0>a:before,.admin__page-nav-item-message .admin__page-nav-item-message-icon,.admin__page-nav-title._collapsible:after,.data-grid-filters-action-wrap .action-default:before,.data-grid-row-changed:after,.data-grid-row-parent>td .data-grid-checkbox-cell-inner:before,.data-grid-search-control-wrap .action-submit:before,.extensions-information .list .extension-delete,.icon-failed:before,.icon-success:before,.notifications-action:before,.notifications-close:before,.page-actions .page-actions-buttons>button.action-back:before,.page-actions .page-actions-buttons>button.back:before,.page-actions>button.action-back:before,.page-actions>button.back:before,.page-title-jumbo-success:before,.search-global-label:before,.selectmenu .action-delete:before,.selectmenu .action-edit:before,.selectmenu .action-save:before,.setup-home-item:before,.sticky-header .data-grid-search-control-wrap .data-grid-search-label:before,.store-switcher .dropdown-menu .dropdown-toolbar a:before,.tooltip .help a:before,.tooltip .help span:before{-webkit-font-smoothing:antialiased;font-family:Icons;font-style:normal;font-weight:400;line-height:1;speak:none}.validation-symbol:after{color:#e22626;content:'*';font-weight:400;margin-left:3px}.abs-modal-overlay,.modals-overlay{background:rgba(0,0,0,.35);bottom:0;left:0;position:fixed;right:0;top:0}.abs-action-delete>span,.abs-visually-hidden,.action-multicheck-wrap .action-multicheck-toggle>span,.admin__actions-switch-checkbox,.admin__control-fields .admin__field:nth-child(n+2):not(.admin__field-option):not(.admin__field-group-show-label)>.admin__field-label,.admin__field-tooltip .admin__field-tooltip-action span,.customize-your-store .customize-your-store-default .legend,.extensions-information .list .extension-delete>span,.form-el-checkbox,.form-el-radio,.selectmenu .action-delete>span,.selectmenu .action-edit>span,.selectmenu .action-save>span,.selectmenu-toggle span,.tooltip .help a span,.tooltip .help span span,[class*=admin__control-grouped]>.admin__field:nth-child(n+2):not(.admin__field-option):not(.admin__field-group-show-label):not(.admin__field-date)>.admin__field-label{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.abs-visually-hidden-reset,.admin__field-group-columns>.admin__field:nth-child(n+2):not(.admin__field-option):not(.admin__field-group-show-label):not(.admin__field-date)>.admin__field-label[class]{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.abs-clearfix:after,.abs-clearfix:before,.action-multicheck-wrap:after,.action-multicheck-wrap:before,.actions-split:after,.actions-split:before,.admin__control-table-pagination:after,.admin__control-table-pagination:before,.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-content:after,.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-content:before,.admin__data-grid-filters-footer:after,.admin__data-grid-filters-footer:before,.admin__data-grid-filters:after,.admin__data-grid-filters:before,.admin__data-grid-header-row:after,.admin__data-grid-header-row:before,.admin__field-complex:after,.admin__field-complex:before,.modal-slide .magento-message .insert-title-inner:after,.modal-slide .magento-message .insert-title-inner:before,.modal-slide .main-col .insert-title-inner:after,.modal-slide .main-col .insert-title-inner:before,.page-actions._fixed:after,.page-actions._fixed:before,.page-content:after,.page-content:before,.page-header-actions:after,.page-header-actions:before,.page-main-actions:not(._hidden):after,.page-main-actions:not(._hidden):before{content:'';display:table}.abs-clearfix:after,.action-multicheck-wrap:after,.actions-split:after,.admin__control-table-pagination:after,.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-content:after,.admin__data-grid-filters-footer:after,.admin__data-grid-filters:after,.admin__data-grid-header-row:after,.admin__field-complex:after,.modal-slide .magento-message .insert-title-inner:after,.modal-slide .main-col .insert-title-inner:after,.page-actions._fixed:after,.page-content:after,.page-header-actions:after,.page-main-actions:not(._hidden):after{clear:both}.abs-list-reset-styles{margin:0;padding:0;list-style:none}.abs-draggable-handle,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .draggable-handle,.admin__control-table .draggable-handle,.data-grid .data-grid-draggable-row-cell .draggable-handle{cursor:-webkit-grab;cursor:move;font-size:0;margin-top:-4px;padding:0 1rem 0 0;vertical-align:middle;display:inline-block;text-decoration:none}.abs-draggable-handle:before,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .draggable-handle:before,.admin__control-table .draggable-handle:before,.data-grid .data-grid-draggable-row-cell .draggable-handle:before{-webkit-font-smoothing:antialiased;font-size:1.8rem;line-height:inherit;color:#9e9e9e;content:'\e617';font-family:Icons;vertical-align:middle;display:inline-block;font-weight:400;overflow:hidden;speak:none;text-align:center}.abs-draggable-handle:hover:before,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .draggable-handle:hover:before,.admin__control-table .draggable-handle:hover:before,.data-grid .data-grid-draggable-row-cell .draggable-handle:hover:before{color:#858585}.abs-config-scope-label,.admin__field:not(.admin__field-option)>.admin__field-label span[data-config-scope]:before{bottom:-1.3rem;color:gray;content:attr(data-config-scope);font-size:1.1rem;font-weight:400;min-width:15rem;position:absolute;right:0;text-transform:lowercase}.abs-word-wrap,.admin__field:not(.admin__field-option)>.admin__field-label{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;box-sizing:border-box}*,:after,:before{box-sizing:inherit}:focus{box-shadow:none;outline:0}._keyfocus :focus{box-shadow:0 0 0 1px #008bdb}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}mark{background:#ff0;color:#000}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}embed,img,object,video{max-width:100%}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@font-face{font-family:'Open Sans';src:url(../fonts/opensans/light/opensans-300.eot);src:url(../fonts/opensans/light/opensans-300.eot?#iefix) format('embedded-opentype'),url(../fonts/opensans/light/opensans-300.woff2) format('woff2'),url(../fonts/opensans/light/opensans-300.woff) format('woff'),url(../fonts/opensans/light/opensans-300.ttf) format('truetype'),url('../fonts/opensans/light/opensans-300.svg#Open Sans') format('svg');font-weight:300;font-style:normal}@font-face{font-family:'Open Sans';src:url(../fonts/opensans/regular/opensans-400.eot);src:url(../fonts/opensans/regular/opensans-400.eot?#iefix) format('embedded-opentype'),url(../fonts/opensans/regular/opensans-400.woff2) format('woff2'),url(../fonts/opensans/regular/opensans-400.woff) format('woff'),url(../fonts/opensans/regular/opensans-400.ttf) format('truetype'),url('../fonts/opensans/regular/opensans-400.svg#Open Sans') format('svg');font-weight:400;font-style:normal}@font-face{font-family:'Open Sans';src:url(../fonts/opensans/semibold/opensans-600.eot);src:url(../fonts/opensans/semibold/opensans-600.eot?#iefix) format('embedded-opentype'),url(../fonts/opensans/semibold/opensans-600.woff2) format('woff2'),url(../fonts/opensans/semibold/opensans-600.woff) format('woff'),url(../fonts/opensans/semibold/opensans-600.ttf) format('truetype'),url('../fonts/opensans/semibold/opensans-600.svg#Open Sans') format('svg');font-weight:600;font-style:normal}@font-face{font-family:'Open Sans';src:url(../fonts/opensans/bold/opensans-700.eot);src:url(../fonts/opensans/bold/opensans-700.eot?#iefix) format('embedded-opentype'),url(../fonts/opensans/bold/opensans-700.woff2) format('woff2'),url(../fonts/opensans/bold/opensans-700.woff) format('woff'),url(../fonts/opensans/bold/opensans-700.ttf) format('truetype'),url('../fonts/opensans/bold/opensans-700.svg#Open Sans') format('svg');font-weight:700;font-style:normal}html{font-size:62.5%}body{color:#333;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.36;font-size:1.4rem}h1{margin:0 0 2rem;color:#41362f;font-weight:400;line-height:1.2;font-size:2.8rem}h2{margin:0 0 2rem;color:#41362f;font-weight:400;line-height:1.2;font-size:2rem}h3{margin:0 0 2rem;color:#41362f;font-weight:600;line-height:1.2;font-size:1.7rem}h4,h5,h6{font-weight:600;margin-top:0}p{margin:0 0 1em}small{font-size:1.2rem}a{color:#008bdb;text-decoration:none}a:hover{color:#0fa7ff;text-decoration:underline}dl,ol,ul{padding-left:0}nav ol,nav ul{list-style:none;margin:0;padding:0}html{height:100%}body{background-color:#fff;min-height:100%;min-width:102.4rem}.page-wrapper{background-color:#fff;display:inline-block;margin-left:-4px;vertical-align:top;width:calc(100% - 8.8rem)}.page-content{padding-bottom:3rem;padding-left:3rem;padding-right:3rem}.notices-wrapper{margin:0 3rem}.notices-wrapper .messages{margin-bottom:0}.row{margin-left:0;margin-right:0}.row:after{clear:both;content:'';display:table}.col-l-1,.col-l-10,.col-l-11,.col-l-12,.col-l-2,.col-l-3,.col-l-4,.col-l-5,.col-l-6,.col-l-7,.col-l-8,.col-l-9,.col-m-1,.col-m-10,.col-m-11,.col-m-12,.col-m-2,.col-m-3,.col-m-4,.col-m-5,.col-m-6,.col-m-7,.col-m-8,.col-m-9,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{min-height:1px;padding-left:0;padding-right:0;position:relative}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}.row-gutter{margin-left:-1.5rem;margin-right:-1.5rem}.row-gutter>[class*=col-]{padding-left:1.5rem;padding-right:1.5rem}.abs-clearer:after,.extension-manager-content:after,.extension-manager-title:after,.form-row:after,.header:after,.nav:after,body:after{clear:both;content:'';display:table}.ng-cloak{display:none!important}.hide.hide{display:none}.show.show{display:block}.text-center{text-align:center}.text-right{text-align:right}@font-face{font-family:Icons;src:url(../fonts/icons/icons.eot);src:url(../fonts/icons/icons.eot?#iefix) format('embedded-opentype'),url(../fonts/icons/icons.woff2) format('woff2'),url(../fonts/icons/icons.woff) format('woff'),url(../fonts/icons/icons.ttf) format('truetype'),url(../fonts/icons/icons.svg#Icons) format('svg');font-weight:400;font-style:normal}[class*=icon-]{display:inline-block;line-height:1}.icon-failed:before,.icon-success:before,[class*=icon-]:after{font-family:Icons}.icon-success{color:#79a22e}.icon-success:before{content:'\e62d'}.icon-failed{color:#e22626}.icon-failed:before{content:'\e632'}.icon-success-thick:after{content:'\e62d'}.icon-collapse:after{content:'\e615'}.icon-failed-thick:after{content:'\e632'}.icon-expand:after{content:'\e616'}.icon-warning:after{content:'\e623'}.icon-failed-round,.icon-success-round{border-radius:100%;color:#fff;font-size:2.5rem;height:1em;position:relative;text-align:center;width:1em}.icon-failed-round:after,.icon-success-round:after{bottom:0;font-size:.5em;left:0;position:absolute;right:0;top:.45em}.icon-success-round{background-color:#79a22e}.icon-success-round:after{content:'\e62d'}.icon-failed-round{background-color:#e22626}.icon-failed-round:after{content:'\e632'}dl,ol,ul{margin-top:0}.list{padding-left:0}.list>li{display:block;margin-bottom:.75em;position:relative}.list>li>.icon-failed,.list>li>.icon-success{font-size:1.6em;left:-.1em;position:absolute;top:0}.list>li>.icon-success{color:#79a22e}.list>li>.icon-failed{color:#e22626}.list-item-failed,.list-item-icon,.list-item-success,.list-item-warning{padding-left:3.5rem}.list-item-failed:before,.list-item-success:before,.list-item-warning:before{left:-.1em;position:absolute}.list-item-success:before{color:#79a22e}.list-item-failed:before{color:#e22626}.list-item-warning:before{color:#ef672f}.list-definition{margin:0 0 3rem;padding:0}.list-definition>dt{clear:left;float:left}.list-definition>dd{margin-bottom:1em;margin-left:20rem}.btn-wrap{margin:0 auto}.btn-wrap .btn{width:100%}.btn{background:#e3e3e3;border:none;color:#514943;display:inline-block;font-size:1.6rem;font-weight:600;padding:.45em .9em;text-align:center}.btn:hover{background-color:#dbdbdb;color:#514943;text-decoration:none}.btn:active{background-color:#d6d6d6}.btn.disabled,.btn[disabled]{cursor:default;opacity:.5;pointer-events:none}.ie9 .btn.disabled,.ie9 .btn[disabled]{background-color:#f0f0f0;opacity:1;text-shadow:none}.btn-large{padding:.75em 1.25em}.btn-medium{font-size:1.4rem;padding:.5em 1.5em .6em}.btn-link{background-color:transparent;border:none;color:#008bdb;font-family:1.6rem;font-size:1.5rem}.btn-link:active,.btn-link:focus,.btn-link:hover{background-color:transparent;color:#0fa7ff}.btn-prime{background-color:#eb5202;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.25)}.btn-prime:focus,.btn-prime:hover{background-color:#f65405;background-repeat:repeat-x;background-image:linear-gradient(to right,#e04f00 0,#f65405 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e04f00', endColorstr='#f65405', GradientType=1);color:#fff}.btn-prime:active{background-color:#e04f00;background-repeat:repeat-x;background-image:linear-gradient(to right,#f65405 0,#e04f00 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f65405', endColorstr='#e04f00', GradientType=1);color:#fff}.ie9 .btn-prime.disabled,.ie9 .btn-prime[disabled]{background-color:#fd6e23}.ie9 .btn-prime.disabled:active,.ie9 .btn-prime.disabled:hover,.ie9 .btn-prime[disabled]:active,.ie9 .btn-prime[disabled]:hover{background-color:#fd6e23;-webkit-filter:none;filter:none}.btn-secondary{background-color:#514943;color:#fff}.btn-secondary:hover{background-color:#5f564f;color:#fff}.btn-secondary:active,.btn-secondary:focus{background-color:#574e48;color:#fff}.ie9 .btn-secondary.disabled,.ie9 .btn-secondary[disabled]{background-color:#514943}.ie9 .btn-secondary.disabled:active,.ie9 .btn-secondary[disabled]:active{background-color:#514943;-webkit-filter:none;filter:none}[class*=btn-wrap-triangle]{overflow:hidden;position:relative}[class*=btn-wrap-triangle] .btn:after{border-style:solid;content:'';height:0;position:absolute;top:0;width:0}.btn-wrap-triangle-right{display:inline-block;padding-right:1.74rem;position:relative}.btn-wrap-triangle-right .btn{text-indent:.92rem}.btn-wrap-triangle-right .btn:after{border-color:transparent transparent transparent #e3e3e3;border-width:1.84rem 0 1.84rem 1.84rem;left:100%;margin-left:-1.74rem}.btn-wrap-triangle-right .btn:focus:after,.btn-wrap-triangle-right .btn:hover:after{border-left-color:#dbdbdb}.btn-wrap-triangle-right .btn:active:after{border-left-color:#d6d6d6}.btn-wrap-triangle-right .btn:not(.disabled):active,.btn-wrap-triangle-right .btn:not([disabled]):active{left:1px}.ie9 .btn-wrap-triangle-right .btn.disabled:after,.ie9 .btn-wrap-triangle-right .btn[disabled]:after{border-color:transparent transparent transparent #f0f0f0}.ie9 .btn-wrap-triangle-right .btn.disabled:active:after,.ie9 .btn-wrap-triangle-right .btn.disabled:focus:after,.ie9 .btn-wrap-triangle-right .btn.disabled:hover:after,.ie9 .btn-wrap-triangle-right .btn[disabled]:active:after,.ie9 .btn-wrap-triangle-right .btn[disabled]:focus:after,.ie9 .btn-wrap-triangle-right .btn[disabled]:hover:after{border-left-color:#f0f0f0}.btn-wrap-triangle-right .btn-prime:after{border-color:transparent transparent transparent #eb5202}.btn-wrap-triangle-right .btn-prime:focus:after,.btn-wrap-triangle-right .btn-prime:hover:after{border-left-color:#f65405}.btn-wrap-triangle-right .btn-prime:active:after{border-left-color:#e04f00}.btn-wrap-triangle-right .btn-prime:not(.disabled):active,.btn-wrap-triangle-right .btn-prime:not([disabled]):active{left:1px}.ie9 .btn-wrap-triangle-right .btn-prime.disabled:after,.ie9 .btn-wrap-triangle-right .btn-prime[disabled]:after{border-color:transparent transparent transparent #fd6e23}.ie9 .btn-wrap-triangle-right .btn-prime.disabled:active:after,.ie9 .btn-wrap-triangle-right .btn-prime.disabled:hover:after,.ie9 .btn-wrap-triangle-right .btn-prime[disabled]:active:after,.ie9 .btn-wrap-triangle-right .btn-prime[disabled]:hover:after{border-left-color:#fd6e23}.btn-wrap-triangle-left{display:inline-block;padding-left:1.74rem}.btn-wrap-triangle-left .btn{text-indent:-.92rem}.btn-wrap-triangle-left .btn:after{border-color:transparent #e3e3e3 transparent transparent;border-width:1.84rem 1.84rem 1.84rem 0;margin-right:-1.74rem;right:100%}.btn-wrap-triangle-left .btn:focus:after,.btn-wrap-triangle-left .btn:hover:after{border-right-color:#dbdbdb}.btn-wrap-triangle-left .btn:active:after{border-right-color:#d6d6d6}.btn-wrap-triangle-left .btn:not(.disabled):active,.btn-wrap-triangle-left .btn:not([disabled]):active{right:1px}.ie9 .btn-wrap-triangle-left .btn.disabled:after,.ie9 .btn-wrap-triangle-left .btn[disabled]:after{border-color:transparent #f0f0f0 transparent transparent}.ie9 .btn-wrap-triangle-left .btn.disabled:active:after,.ie9 .btn-wrap-triangle-left .btn.disabled:hover:after,.ie9 .btn-wrap-triangle-left .btn[disabled]:active:after,.ie9 .btn-wrap-triangle-left .btn[disabled]:hover:after{border-right-color:#f0f0f0}.btn-wrap-triangle-left .btn-prime:after{border-color:transparent #eb5202 transparent transparent}.btn-wrap-triangle-left .btn-prime:focus:after,.btn-wrap-triangle-left .btn-prime:hover:after{border-right-color:#e04f00}.btn-wrap-triangle-left .btn-prime:active:after{border-right-color:#f65405}.btn-wrap-triangle-left .btn-prime:not(.disabled):active,.btn-wrap-triangle-left .btn-prime:not([disabled]):active{right:1px}.ie9 .btn-wrap-triangle-left .btn-prime.disabled:after,.ie9 .btn-wrap-triangle-left .btn-prime[disabled]:after{border-color:transparent #fd6e23 transparent transparent}.ie9 .btn-wrap-triangle-left .btn-prime.disabled:active:after,.ie9 .btn-wrap-triangle-left .btn-prime.disabled:hover:after,.ie9 .btn-wrap-triangle-left .btn-prime[disabled]:active:after,.ie9 .btn-wrap-triangle-left .btn-prime[disabled]:hover:after{border-right-color:#fd6e23}.btn-expand{background-color:transparent;border:none;color:#303030;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.4rem;font-weight:700;padding:0;position:relative}.btn-expand.expanded:after{border-color:transparent transparent #303030;border-width:0 .285em .36em}.btn-expand.expanded:hover:after{border-color:transparent transparent #3d3d3d}.btn-expand:hover{background-color:transparent;border:none;color:#3d3d3d}.btn-expand:hover:after{border-color:#3d3d3d transparent transparent}.btn-expand:after{border-color:#303030 transparent transparent;border-style:solid;border-width:.36em .285em 0;content:'';height:0;left:100%;margin-left:.5em;margin-top:-.18em;position:absolute;top:50%;width:0}[class*=col-] .form-el-input,[class*=col-] .form-el-select{width:100%}.form-fieldset{border:none;margin:0 0 1em;padding:0}.form-row{margin-bottom:2.2rem}.form-row .form-row{margin-bottom:.4rem}.form-row .form-label{display:block;font-weight:600;padding:.6rem 2.1em 0 0;text-align:right}.form-row .form-label.required{position:relative}.form-row .form-label.required:after{color:#eb5202;content:'*';font-size:1.15em;position:absolute;right:.7em;top:.5em}.form-row .form-el-checkbox+.form-label:before,.form-row .form-el-radio+.form-label:before{top:.7rem}.form-row .form-el-checkbox+.form-label:after,.form-row .form-el-radio+.form-label:after{top:1.1rem}.form-row.form-row-text{padding-top:.6rem}.form-row.form-row-text .action-sign-out{font-size:1.2rem;margin-left:1rem}.form-note{font-size:1.2rem;font-weight:600;margin-top:1rem}.form-el-dummy{display:none}.fieldset{border:0;margin:0;min-width:0;padding:0}input:not([disabled]):focus,textarea:not([disabled]):focus{box-shadow:none}.form-el-input{border:1px solid #adadad;color:#303030;padding:.35em .55em .5em}.form-el-input:hover{border-color:#949494}.form-el-input:focus{border-color:#008bdb}.form-el-input:required{box-shadow:none}.form-label{margin-bottom:.5em}[class*=form-label][for]{cursor:pointer}.form-el-insider-wrap{display:table;width:100%}.form-el-insider-input{display:table-cell;width:100%}.form-el-insider{border-radius:2px;display:table-cell;padding:.43em .55em .5em 0;vertical-align:top}.form-legend,.form-legend-expand,.form-legend-light{display:block;margin:0}.form-legend,.form-legend-expand{font-size:1.25em;font-weight:600;margin-bottom:2.5em;padding-top:1.5em}.form-legend{border-top:1px solid #ccc;width:100%}.form-legend-light{font-size:1em;margin-bottom:1.5em}.form-legend-expand{cursor:pointer;transition:opacity .2s linear}.form-legend-expand:hover{opacity:.85}.form-legend-expand.expanded:after{content:'\e615'}.form-legend-expand:after{content:'\e616';font-family:Icons;font-size:1.15em;font-weight:400;margin-left:.5em;vertical-align:sub}.form-el-checkbox.disabled+.form-label,.form-el-checkbox.disabled+.form-label:before,.form-el-checkbox[disabled]+.form-label,.form-el-checkbox[disabled]+.form-label:before,.form-el-radio.disabled+.form-label,.form-el-radio.disabled+.form-label:before,.form-el-radio[disabled]+.form-label,.form-el-radio[disabled]+.form-label:before{cursor:default;opacity:.5;pointer-events:none}.form-el-checkbox:not(.disabled)+.form-label:hover:before,.form-el-checkbox:not([disabled])+.form-label:hover:before,.form-el-radio:not(.disabled)+.form-label:hover:before,.form-el-radio:not([disabled])+.form-label:hover:before{border-color:#514943}.form-el-checkbox+.form-label,.form-el-radio+.form-label{font-weight:400;padding-left:2em;padding-right:0;position:relative;text-align:left;transition:border-color .1s linear}.form-el-checkbox+.form-label:before,.form-el-radio+.form-label:before{border:1px solid;content:'';left:0;position:absolute;top:.1rem;transition:border-color .1s linear}.form-el-checkbox+.form-label:before{background-color:#fff;border-color:#adadad;border-radius:2px;font-size:1.2rem;height:1.6rem;line-height:1.2;width:1.6rem}.form-el-checkbox:checked+.form-label::before{content:'\e62d';font-family:Icons}.form-el-radio+.form-label:before{background-color:#fff;border:1px solid #adadad;border-radius:100%;height:1.8rem;width:1.8rem}.form-el-radio+.form-label:after{background:0 0;border:.5rem solid transparent;border-radius:100%;content:'';height:0;left:.4rem;position:absolute;top:.5rem;transition:background .3s linear;width:0}.form-el-radio:checked+.form-label{cursor:default}.form-el-radio:checked+.form-label:after{border-color:#514943}.form-select-label{border:1px solid #adadad;color:#303030;cursor:pointer;display:block;overflow:hidden;position:relative;z-index:0}.form-select-label:hover,.form-select-label:hover:after{border-color:#949494}.form-select-label:active,.form-select-label:active:after,.form-select-label:focus,.form-select-label:focus:after{border-color:#008bdb}.form-select-label:after{background:#e3e3e3;border-left:1px solid #adadad;bottom:0;content:'';position:absolute;right:0;top:0;width:2.36em;z-index:-2}.ie9 .form-select-label:after{display:none}.form-select-label:before{border-color:#303030 transparent transparent;border-style:solid;border-width:5px 4px 0;content:'';height:0;margin-right:-4px;margin-top:-2.5px;position:absolute;right:1.18em;top:50%;width:0;z-index:-1}.ie9 .form-select-label:before{display:none}.form-select-label .form-el-select{background:0 0;border:none;border-radius:0;content:'';display:block;margin:0;padding:.35em calc(2.36em + 10%) .5em .55em;width:110%}.ie9 .form-select-label .form-el-select{padding-right:.55em;width:100%}.form-select-label .form-el-select::-ms-expand{display:none}.form-el-select{background:#fff;border:1px solid #adadad;border-radius:2px;color:#303030;display:block;padding:.35em .55em}.multiselect-custom{border:1px solid #adadad;height:45.2rem;margin:0 0 1.5rem;overflow:auto;position:relative}.multiselect-custom ul{margin:0;padding:0;list-style:none;min-width:29rem}.multiselect-custom .item{padding:1rem 1.4rem}.multiselect-custom .selected{background-color:#e0f6fe}.multiselect-custom .form-label{margin-bottom:0}[class*=form-el-].invalid{border-color:#e22626}[class*=form-el-].invalid+.error-container{display:block}.error-container{background-color:#fffbbb;border:1px solid #ee7d7d;color:#514943;display:none;font-size:1.19rem;margin-top:.2rem;padding:.8rem 1rem .9rem}.check-result-message{margin-left:.5em;min-height:3.68rem;-ms-align-items:center;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex}.check-result-text{margin-left:.5em}body:not([class]){min-width:0}.container{display:block;margin:0 auto 4rem;max-width:100rem;padding:0}.abs-action-delete,.action-close:before,.action-next:before,.action-previous:before,.admin-user .admin__action-dropdown:before,.admin__action-multiselect-dropdown:before,.admin__action-multiselect-search-label:before,.admin__control-checkbox+label:before,.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete:before,.admin__control-table .action-delete:before,.admin__current-filters-list .action-remove:before,.admin__data-grid-action-bookmarks .action-delete:before,.admin__data-grid-action-bookmarks .action-edit:before,.admin__data-grid-action-bookmarks .action-submit:before,.admin__data-grid-action-bookmarks .admin__action-dropdown:before,.admin__data-grid-action-columns .admin__action-dropdown:before,.admin__data-grid-action-export .admin__action-dropdown:before,.admin__field-fallback-reset:before,.admin__menu .level-0>a:before,.admin__page-nav-item-message .admin__page-nav-item-message-icon,.admin__page-nav-title._collapsible:after,.data-grid-filters-action-wrap .action-default:before,.data-grid-row-changed:after,.data-grid-row-parent>td .data-grid-checkbox-cell-inner:before,.data-grid-search-control-wrap .action-submit:before,.extensions-information .list .extension-delete,.icon-failed:before,.icon-success:before,.notifications-action:before,.notifications-close:before,.page-actions .page-actions-buttons>button.action-back:before,.page-actions .page-actions-buttons>button.back:before,.page-actions>button.action-back:before,.page-actions>button.back:before,.page-title-jumbo-success:before,.search-global-label:before,.selectmenu .action-delete:before,.selectmenu .action-edit:before,.selectmenu .action-save:before,.setup-home-item:before,.sticky-header .data-grid-search-control-wrap .data-grid-search-label:before,.store-switcher .dropdown-menu .dropdown-toolbar a:before,.tooltip .help a:before,.tooltip .help span:before{-webkit-font-smoothing:antialiased;font-family:Icons;font-style:normal;font-weight:400;line-height:1;speak:none}.text-stretch{margin-bottom:1.5em}.page-title-jumbo{font-size:4rem;font-weight:300;letter-spacing:-.05em;margin-bottom:2.9rem}.page-title-jumbo-success:before{color:#79a22e;content:'\e62d';font-size:3.9rem;margin-left:-.3rem;margin-right:2.4rem}.list{margin-bottom:3rem}.list-dot .list-item{display:list-item;list-style-position:inside;margin-bottom:1.2rem}.list-title{color:#333;font-size:1.4rem;font-weight:700;letter-spacing:.025em;margin-bottom:1.2rem}.list-item-failed:before,.list-item-success:before,.list-item-warning:before{font-family:Icons;font-size:1.6rem;top:0}.list-item-success:before{content:'\e62d';font-size:1.6rem}.list-item-failed:before{content:'\e632';font-size:1.4rem;left:.1rem;top:.2rem}.list-item-warning:before{content:'\e623';font-size:1.3rem;left:.2rem}.form-wrap{margin-bottom:3.6rem;padding-top:2.1rem}.form-el-label-horizontal{display:inline-block;font-size:1.3rem;font-weight:600;letter-spacing:.025em;margin-bottom:.4rem;margin-left:.4rem}.app-updater{min-width:768px}body._has-modal{height:100%;overflow:hidden;width:100%}.modals-overlay{z-index:899}.modal-popup,.modal-slide{bottom:0;min-width:0;position:fixed;right:0;top:0;visibility:hidden}.modal-popup._show,.modal-slide._show{visibility:visible}.modal-popup._show .modal-inner-wrap,.modal-slide._show .modal-inner-wrap{-ms-transform:translate(0,0);transform:translate(0,0)}.modal-popup .modal-inner-wrap,.modal-slide .modal-inner-wrap{background-color:#fff;box-shadow:0 0 12px 2px rgba(0,0,0,.35);opacity:1;pointer-events:auto}.modal-slide{left:14.8rem;z-index:900}.modal-slide._show .modal-inner-wrap{-ms-transform:translateX(0);transform:translateX(0)}.modal-slide .modal-inner-wrap{height:100%;overflow-y:auto;position:static;-ms-transform:translateX(100%);transform:translateX(100%);transition-duration:.3s;transition-property:transform,visibility;transition-timing-function:ease-in-out;width:auto}.modal-slide._inner-scroll .modal-inner-wrap{overflow-y:visible;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.modal-slide._inner-scroll .modal-footer,.modal-slide._inner-scroll .modal-header{-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.modal-slide._inner-scroll .modal-content{overflow-y:auto}.modal-slide._inner-scroll .modal-footer{margin-top:auto}.modal-slide .modal-content,.modal-slide .modal-footer,.modal-slide .modal-header{padding:0 2.6rem 2.6rem}.modal-slide .modal-header{padding-bottom:2.1rem;padding-top:2.1rem}.modal-popup{z-index:900;left:0;overflow-y:auto}.modal-popup._show .modal-inner-wrap{-ms-transform:translateY(0);transform:translateY(0)}.modal-popup .modal-inner-wrap{margin:5rem auto;width:75%;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;box-sizing:border-box;height:auto;left:0;position:absolute;right:0;-ms-transform:translateY(-200%);transform:translateY(-200%);transition-duration:.2s;transition-property:transform,visibility;transition-timing-function:ease}.modal-popup._inner-scroll{overflow-y:visible}.ie10 .modal-popup._inner-scroll,.ie9 .modal-popup._inner-scroll{overflow-y:auto}.modal-popup._inner-scroll .modal-inner-wrap{max-height:90%}.ie10 .modal-popup._inner-scroll .modal-inner-wrap,.ie9 .modal-popup._inner-scroll .modal-inner-wrap{max-height:none}.modal-popup._inner-scroll .modal-content{overflow-y:auto}.modal-popup .modal-content,.modal-popup .modal-footer,.modal-popup .modal-header{padding-left:3rem;padding-right:3rem}.modal-popup .modal-footer,.modal-popup .modal-header{-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.modal-popup .modal-header{padding-bottom:1.2rem;padding-top:3rem}.modal-popup .modal-footer{margin-top:auto;padding-bottom:3rem}.modal-popup .modal-footer-actions{text-align:right}.admin__action-dropdown-wrap{display:inline-block;position:relative}.admin__action-dropdown-wrap .admin__action-dropdown-text:after{left:-6px;right:0}.admin__action-dropdown-wrap .admin__action-dropdown-menu{left:auto;right:0}.admin__action-dropdown-wrap._active .admin__action-dropdown,.admin__action-dropdown-wrap.active .admin__action-dropdown{border-color:#007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5)}.admin__action-dropdown-wrap._active .admin__action-dropdown-text:after,.admin__action-dropdown-wrap.active .admin__action-dropdown-text:after{background-color:#fff;content:'';height:6px;position:absolute;top:100%}.admin__action-dropdown-wrap._active .admin__action-dropdown-menu,.admin__action-dropdown-wrap.active .admin__action-dropdown-menu{display:block}.admin__action-dropdown-wrap._disabled .admin__action-dropdown{cursor:default}.admin__action-dropdown-wrap._disabled:hover .admin__action-dropdown{color:#333}.admin__action-dropdown{background-color:#fff;border:1px solid transparent;border-bottom:none;border-radius:0;box-shadow:none;color:#333;display:inline-block;font-size:1.3rem;font-weight:400;letter-spacing:-.025em;padding:.7rem 3.3rem .8rem 1.5rem;position:relative;vertical-align:baseline;z-index:2}.admin__action-dropdown._active:after,.admin__action-dropdown.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.admin__action-dropdown:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;top:50%;transition:all .2s linear;width:0}._active .admin__action-dropdown:after,.active .admin__action-dropdown:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.admin__action-dropdown:hover:after{border-color:#000 transparent transparent}.admin__action-dropdown:focus,.admin__action-dropdown:hover{background-color:#fff;color:#000;text-decoration:none}.admin__action-dropdown:after{right:1.5rem}.admin__action-dropdown:before{margin-right:1rem}.admin__action-dropdown-menu{background-color:#fff;border:1px solid #007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5);display:none;line-height:1.36;margin-top:-1px;min-width:120%;padding:.5rem 1rem;position:absolute;top:100%;transition:all .15s ease;z-index:1}.admin__action-dropdown-menu>li{display:block}.admin__action-dropdown-menu>li>a{color:#333;display:block;text-decoration:none;padding:.6rem .5rem}.selectmenu{display:inline-block;position:relative;text-align:left;z-index:1}.selectmenu._active{border-color:#007bdb;z-index:500}.selectmenu .action-delete,.selectmenu .action-edit,.selectmenu .action-save{background-color:transparent;border-color:transparent;box-shadow:none;padding:0 1rem}.selectmenu .action-delete:hover,.selectmenu .action-edit:hover,.selectmenu .action-save:hover{background-color:transparent;border-color:transparent;box-shadow:none}.selectmenu .action-delete:before,.selectmenu .action-edit:before,.selectmenu .action-save:before{content:'\e630'}.selectmenu .action-delete,.selectmenu .action-edit{border:0 solid #fff;border-left-width:1px;bottom:0;position:absolute;right:0;top:0;z-index:1}.selectmenu .action-delete:hover,.selectmenu .action-edit:hover{border:0 solid #fff;border-left-width:1px}.selectmenu .action-save:before{content:'\e625'}.selectmenu .action-edit:before{content:'\e631'}.selectmenu-value{display:inline-block}.selectmenu-value input[type=text]{-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none;border:0;display:inline;margin:0;width:6rem}body._keyfocus .selectmenu-value input[type=text]:focus{box-shadow:none}.selectmenu-toggle{padding-right:3rem;background:0 0;border-width:0;bottom:0;float:right;position:absolute;right:0;top:0;width:0}.selectmenu-toggle._active:after,.selectmenu-toggle.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.selectmenu-toggle:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;right:1.1rem;top:50%;transition:all .2s linear;width:0}._active .selectmenu-toggle:after,.active .selectmenu-toggle:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.selectmenu-toggle:hover:after{border-color:#000 transparent transparent}.selectmenu-toggle:active,.selectmenu-toggle:focus,.selectmenu-toggle:hover{background:0 0}.selectmenu._active .selectmenu-toggle:before{border-color:#007bdb}body._keyfocus .selectmenu-toggle:focus{box-shadow:none}.selectmenu-toggle:before{background:#e3e3e3;border-left:1px solid #adadad;bottom:0;content:'';display:block;position:absolute;right:0;top:0;width:3.2rem}.selectmenu-items{background:#fff;border:1px solid #007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5);display:none;float:left;left:-1px;margin-top:3px;max-width:20rem;min-width:calc(100% + 2px);position:absolute;top:100%}.selectmenu-items._active{display:block}.selectmenu-items ul{float:left;list-style-type:none;margin:0;min-width:100%;padding:0}.selectmenu-items li{-webkit-flex-direction:row;display:flex;-ms-flex-direction:row;flex-direction:row;transition:background .2s linear}.selectmenu-items li:hover{background:#e3e3e3}.selectmenu-items li:last-child .selectmenu-item-action,.selectmenu-items li:last-child .selectmenu-item-action:visited{color:#008bdb;text-decoration:none}.selectmenu-items li:last-child .selectmenu-item-action:hover{color:#0fa7ff;text-decoration:underline}.selectmenu-items li:last-child .selectmenu-item-action:active{color:#ff5501;text-decoration:underline}.selectmenu-item{position:relative;width:100%;z-index:1}li._edit>.selectmenu-item{display:none}.selectmenu-item-edit{display:none;padding:.3rem 4rem .3rem .4rem;position:relative;white-space:nowrap;z-index:1}li:last-child .selectmenu-item-edit{padding-right:.4rem}.selectmenu-item-edit .admin__control-text{margin:0;width:5.4rem}li._edit .selectmenu-item-edit{display:block}.selectmenu-item-action{-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none;background:0 0;border:0;color:#333;display:block;font-size:1.4rem;font-weight:400;min-width:100%;padding:1rem 6rem 1rem 1.5rem;text-align:left;transition:background .2s linear;width:5rem}.selectmenu-item-action:focus,.selectmenu-item-action:hover{background:#e3e3e3}.abs-actions-split-xl .action-default,.page-actions .actions-split .action-default{margin-right:4rem}.abs-actions-split-xl .action-toggle,.page-actions .actions-split .action-toggle{padding-right:4rem}.abs-actions-split-xl .action-toggle:after,.page-actions .actions-split .action-toggle:after{border-width:.9rem .6rem 0;margin-top:-.3rem;right:1.4rem}.actions-split{position:relative;z-index:400}.actions-split._active,.actions-split.active,.actions-split:hover{box-shadow:0 0 0 1px #007bdb}.actions-split._active .action-toggle.action-primary,.actions-split._active .action-toggle.primary,.actions-split.active .action-toggle.action-primary,.actions-split.active .action-toggle.primary{background-color:#ba4000;border-color:#ba4000}.actions-split._active .dropdown-menu,.actions-split.active .dropdown-menu{opacity:1;visibility:visible;display:block}.actions-split .action-default,.actions-split .action-toggle{float:left;margin:0}.actions-split .action-default._active,.actions-split .action-default.active,.actions-split .action-default:hover,.actions-split .action-toggle._active,.actions-split .action-toggle.active,.actions-split .action-toggle:hover{box-shadow:none}.actions-split .action-default{margin-right:3.2rem;min-width:9.3rem}.actions-split .action-toggle{padding-right:3.2rem;border-left-color:rgba(0,0,0,.2);bottom:0;padding-left:0;position:absolute;right:0;top:0}.actions-split .action-toggle._active:after,.actions-split .action-toggle.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.actions-split .action-toggle:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;right:1.2rem;top:50%;transition:all .2s linear;width:0}._active .actions-split .action-toggle:after,.active .actions-split .action-toggle:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.actions-split .action-toggle:hover:after{border-color:#000 transparent transparent}.actions-split .action-toggle.action-primary:after,.actions-split .action-toggle.action-secondary:after,.actions-split .action-toggle.primary:after,.actions-split .action-toggle.secondary:after{border-color:#fff transparent transparent}.actions-split .action-toggle>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.action-select-wrap{display:inline-block;position:relative}.action-select-wrap .action-select{padding-right:3.2rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background-color:#fff;font-weight:400;text-align:left}.action-select-wrap .action-select._active:after,.action-select-wrap .action-select.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-select-wrap .action-select:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;right:1.2rem;top:50%;transition:all .2s linear;width:0}._active .action-select-wrap .action-select:after,.active .action-select-wrap .action-select:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-select-wrap .action-select:hover:after{border-color:#000 transparent transparent}.action-select-wrap .action-select:hover,.action-select-wrap .action-select:hover:before{border-color:#878787}.action-select-wrap .action-select:before{background-color:#e3e3e3;border:1px solid #adadad;bottom:0;content:'';position:absolute;right:0;top:0;width:3.2rem}.action-select-wrap .action-select._active{border-color:#007bdb}.action-select-wrap .action-select._active:before{border-color:#007bdb #007bdb #007bdb #adadad}.action-select-wrap .action-select[disabled]{color:#333}.action-select-wrap .action-select[disabled]:after{border-color:#333 transparent transparent}.action-select-wrap._active{z-index:500}.action-select-wrap._active .action-select,.action-select-wrap._active .action-select:before{border-color:#007bdb}.action-select-wrap._active .action-select:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-select-wrap .abs-action-menu .action-submenu,.action-select-wrap .abs-action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu,.action-select-wrap .action-menu .action-submenu,.action-select-wrap .actions-split .action-menu .action-submenu,.action-select-wrap .actions-split .action-menu .action-submenu .action-submenu,.action-select-wrap .actions-split .dropdown-menu .action-submenu,.action-select-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{max-height:45rem;overflow-y:auto}.action-select-wrap .abs-action-menu .action-submenu ._disabled:hover,.action-select-wrap .abs-action-menu .action-submenu .action-submenu ._disabled:hover,.action-select-wrap .action-menu ._disabled:hover,.action-select-wrap .action-menu .action-submenu ._disabled:hover,.action-select-wrap .actions-split .action-menu .action-submenu ._disabled:hover,.action-select-wrap .actions-split .action-menu .action-submenu .action-submenu ._disabled:hover,.action-select-wrap .actions-split .dropdown-menu .action-submenu ._disabled:hover,.action-select-wrap .actions-split .dropdown-menu .action-submenu .action-submenu ._disabled:hover{background:#fff}.action-select-wrap .abs-action-menu .action-submenu ._disabled .action-menu-item,.action-select-wrap .abs-action-menu .action-submenu .action-submenu ._disabled .action-menu-item,.action-select-wrap .action-menu ._disabled .action-menu-item,.action-select-wrap .action-menu .action-submenu ._disabled .action-menu-item,.action-select-wrap .actions-split .action-menu .action-submenu ._disabled .action-menu-item,.action-select-wrap .actions-split .action-menu .action-submenu .action-submenu ._disabled .action-menu-item,.action-select-wrap .actions-split .dropdown-menu .action-submenu ._disabled .action-menu-item,.action-select-wrap .actions-split .dropdown-menu .action-submenu .action-submenu ._disabled .action-menu-item{cursor:default;opacity:.5}.action-select-wrap .action-menu-items{left:0;position:absolute;right:0;top:100%}.action-select-wrap .action-menu-items>.abs-action-menu .action-submenu,.action-select-wrap .action-menu-items>.abs-action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.action-menu,.action-select-wrap .action-menu-items>.action-menu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .action-menu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .dropdown-menu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .dropdown-menu .action-submenu .action-submenu{min-width:100%;position:static}.action-select-wrap .action-menu-items>.abs-action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.abs-action-menu .action-submenu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.action-menu .action-submenu,.action-select-wrap .action-menu-items>.action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .action-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .action-menu .action-submenu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .dropdown-menu .action-submenu .action-submenu,.action-select-wrap .action-menu-items>.actions-split .dropdown-menu .action-submenu .action-submenu .action-submenu{position:absolute}.action-multicheck-wrap{display:inline-block;height:1.6rem;padding-top:1px;position:relative;width:3.1rem;z-index:200}.action-multicheck-wrap:hover .action-multicheck-toggle,.action-multicheck-wrap:hover .admin__control-checkbox+label:before{border-color:#878787}.action-multicheck-wrap._active .action-multicheck-toggle,.action-multicheck-wrap._active .admin__control-checkbox+label:before{border-color:#007bdb}.action-multicheck-wrap._active .abs-action-menu .action-submenu,.action-multicheck-wrap._active .abs-action-menu .action-submenu .action-submenu,.action-multicheck-wrap._active .action-menu,.action-multicheck-wrap._active .action-menu .action-submenu,.action-multicheck-wrap._active .actions-split .action-menu .action-submenu,.action-multicheck-wrap._active .actions-split .action-menu .action-submenu .action-submenu,.action-multicheck-wrap._active .actions-split .dropdown-menu .action-submenu,.action-multicheck-wrap._active .actions-split .dropdown-menu .action-submenu .action-submenu{opacity:1;visibility:visible;display:block}.action-multicheck-wrap._disabled .admin__control-checkbox+label:before{background-color:#fff}.action-multicheck-wrap._disabled .action-multicheck-toggle,.action-multicheck-wrap._disabled .admin__control-checkbox+label:before{border-color:#adadad;opacity:1}.action-multicheck-wrap .action-multicheck-toggle,.action-multicheck-wrap .admin__control-checkbox,.action-multicheck-wrap .admin__control-checkbox+label{float:left}.action-multicheck-wrap .action-multicheck-toggle{border-radius:0 1px 1px 0;height:1.6rem;margin-left:-1px;padding:0;position:relative;transition:border-color .1s linear;width:1.6rem}.action-multicheck-wrap .action-multicheck-toggle._active:after,.action-multicheck-wrap .action-multicheck-toggle.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-multicheck-wrap .action-multicheck-toggle:after{border-color:#000 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;top:50%;transition:all .2s linear;width:0}._active .action-multicheck-wrap .action-multicheck-toggle:after,.active .action-multicheck-wrap .action-multicheck-toggle:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.action-multicheck-wrap .action-multicheck-toggle:hover:after{border-color:#000 transparent transparent}.action-multicheck-wrap .action-multicheck-toggle:focus{border-color:#007bdb}.action-multicheck-wrap .action-multicheck-toggle:after{right:.3rem}.action-multicheck-wrap .abs-action-menu .action-submenu,.action-multicheck-wrap .abs-action-menu .action-submenu .action-submenu,.action-multicheck-wrap .action-menu,.action-multicheck-wrap .action-menu .action-submenu,.action-multicheck-wrap .actions-split .action-menu .action-submenu,.action-multicheck-wrap .actions-split .action-menu .action-submenu .action-submenu,.action-multicheck-wrap .actions-split .dropdown-menu .action-submenu,.action-multicheck-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{left:-1.1rem;margin-top:1px;right:auto;text-align:left}.action-multicheck-wrap .action-menu-item{white-space:nowrap}.admin__action-multiselect-wrap{display:block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.admin__action-multiselect-wrap.action-select-wrap:focus{box-shadow:none}.admin__action-multiselect-wrap.action-select-wrap .abs-action-menu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .abs-action-menu .action-submenu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .action-menu,.admin__action-multiselect-wrap.action-select-wrap .action-menu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .actions-split .action-menu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .actions-split .action-menu .action-submenu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .actions-split .dropdown-menu .action-submenu,.admin__action-multiselect-wrap.action-select-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{max-height:none;overflow-y:inherit}.admin__action-multiselect-wrap .action-menu-item{transition:background-color .1s linear}.admin__action-multiselect-wrap .action-menu-item._selected{background-color:#e0f6fe}.admin__action-multiselect-wrap .action-menu-item._hover{background-color:#e3e3e3}.admin__action-multiselect-wrap .action-menu-item._unclickable{cursor:default}.admin__action-multiselect-wrap .admin__action-multiselect{border:1px solid #adadad;cursor:pointer;display:block;min-height:3.2rem;padding-right:3.6rem;white-space:normal}.admin__action-multiselect-wrap .admin__action-multiselect:after{bottom:1.25rem;top:auto}.admin__action-multiselect-wrap .admin__action-multiselect:before{height:3.3rem;top:auto}.admin__control-table-wrapper .admin__action-multiselect-wrap{position:static}.admin__control-table-wrapper .admin__action-multiselect-wrap .admin__action-multiselect{position:relative}.admin__control-table-wrapper .admin__action-multiselect-wrap .admin__action-multiselect:before{right:-1px;top:-1px}.admin__control-table-wrapper .admin__action-multiselect-wrap .abs-action-menu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .abs-action-menu .action-submenu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .action-menu,.admin__control-table-wrapper .admin__action-multiselect-wrap .action-menu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .actions-split .action-menu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .actions-split .action-menu .action-submenu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .actions-split .dropdown-menu .action-submenu,.admin__control-table-wrapper .admin__action-multiselect-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{left:auto;min-width:34rem;right:auto;top:auto;z-index:1}.admin__action-multiselect-wrap .admin__action-multiselect-item-path{color:#a79d95;font-size:1.2rem;font-weight:400;padding-left:1rem}.admin__action-multiselect-actions-wrap{border-top:1px solid #e3e3e3;margin:0 1rem;padding:1rem 0;text-align:center}.admin__action-multiselect-actions-wrap .action-default{font-size:1.3rem;min-width:13rem}.admin__action-multiselect-text{padding:.6rem 1rem}.abs-action-menu .action-submenu,.abs-action-menu .action-submenu .action-submenu,.action-menu,.action-menu .action-submenu,.actions-split .action-menu .action-submenu,.actions-split .action-menu .action-submenu .action-submenu,.actions-split .dropdown-menu .action-submenu,.actions-split .dropdown-menu .action-submenu .action-submenu{text-align:left}.admin__action-multiselect-label{cursor:pointer;position:relative;z-index:1}.admin__action-multiselect-label:before{margin-right:.5rem}._unclickable .admin__action-multiselect-label{cursor:default;font-weight:700}.admin__action-multiselect-search-wrap{border-bottom:1px solid #e3e3e3;margin:0 1rem;padding:1rem 0;position:relative}.admin__action-multiselect-search{padding-right:3rem;width:100%}.admin__action-multiselect-search-label{display:block;font-size:1.5rem;height:1em;overflow:hidden;position:absolute;right:2.2rem;top:1.7rem;width:1em}.admin__action-multiselect-search-label:before{content:'\e60c'}.admin__action-multiselect-search-count{color:#a79d95;margin-top:1rem}.admin__action-multiselect-menu-inner{margin-bottom:0;max-height:46rem;overflow-y:auto}.admin__action-multiselect-menu-inner .admin__action-multiselect-menu-inner{list-style:none;max-height:none;overflow:hidden;padding-left:2.2rem}.admin__action-multiselect-menu-inner ._hidden{display:none}.admin__action-multiselect-crumb{background-color:#f5f5f5;border:1px solid #a79d95;border-radius:1px;display:inline-block;font-size:1.2rem;margin:.3rem -4px .3rem .3rem;padding:.3rem 2.4rem .4rem 1rem;position:relative;transition:border-color .1s linear}.admin__action-multiselect-crumb:hover{border-color:#908379}.admin__action-multiselect-crumb .action-close{bottom:0;font-size:.5em;position:absolute;right:0;top:0;width:2rem}.admin__action-multiselect-crumb .action-close:hover{color:#000}.admin__action-multiselect-crumb .action-close:active,.admin__action-multiselect-crumb .action-close:focus{background-color:transparent}.admin__action-multiselect-crumb .action-close:active{-ms-transform:scale(0.9);transform:scale(0.9)}.admin__action-multiselect-tree .abs-action-menu .action-submenu,.admin__action-multiselect-tree .abs-action-menu .action-submenu .action-submenu,.admin__action-multiselect-tree .action-menu,.admin__action-multiselect-tree .action-menu .action-submenu,.admin__action-multiselect-tree .actions-split .action-menu .action-submenu,.admin__action-multiselect-tree .actions-split .action-menu .action-submenu .action-submenu,.admin__action-multiselect-tree .actions-split .dropdown-menu .action-submenu,.admin__action-multiselect-tree .actions-split .dropdown-menu .action-submenu .action-submenu{min-width:34.7rem}.admin__action-multiselect-tree .abs-action-menu .action-submenu .action-menu-item,.admin__action-multiselect-tree .abs-action-menu .action-submenu .action-submenu .action-menu-item,.admin__action-multiselect-tree .action-menu .action-menu-item,.admin__action-multiselect-tree .action-menu .action-submenu .action-menu-item,.admin__action-multiselect-tree .actions-split .action-menu .action-submenu .action-menu-item,.admin__action-multiselect-tree .actions-split .action-menu .action-submenu .action-submenu .action-menu-item,.admin__action-multiselect-tree .actions-split .dropdown-menu .action-submenu .action-menu-item,.admin__action-multiselect-tree .actions-split .dropdown-menu .action-submenu .action-submenu .action-menu-item{margin-top:.1rem}.admin__action-multiselect-tree .action-menu-item{margin-left:4.2rem;position:relative}.admin__action-multiselect-tree .action-menu-item._expended:before{border-left:1px dashed #a79d95;bottom:0;content:'';left:-1rem;position:absolute;top:1rem;width:1px}.admin__action-multiselect-tree .action-menu-item._expended .admin__action-multiselect-dropdown:before{content:'\e615'}.admin__action-multiselect-tree .action-menu-item._with-checkbox .admin__action-multiselect-label{padding-left:2.6rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner{position:relative}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner .admin__action-multiselect-menu-inner{padding-left:3.2rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner .admin__action-multiselect-menu-inner:before{left:4.3rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item{position:relative}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:last-child:before{height:2.1rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:after,.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:before{content:'';left:0;position:absolute}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:after{border-top:1px dashed #a79d95;height:1px;top:2.1rem;width:5.2rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item:before{border-left:1px dashed #a79d95;height:100%;top:0;width:1px}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._parent:after{width:4.2rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root{margin-left:-1rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root:after{left:3.2rem;width:2.2rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root:before{left:3.2rem;top:1rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root._parent:after{display:none}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root:first-child:before{top:2.1rem}.admin__action-multiselect-tree .admin__action-multiselect-menu-inner-item._root:last-child:before{height:1rem}.admin__action-multiselect-tree .admin__action-multiselect-label{line-height:2.2rem;vertical-align:middle;word-break:break-all}.admin__action-multiselect-tree .admin__action-multiselect-label:before{left:0;position:absolute;top:.4rem}.admin__action-multiselect-dropdown{border-radius:50%;height:2.2rem;left:-2.2rem;position:absolute;top:1rem;width:2.2rem;z-index:1}.admin__action-multiselect-dropdown:before{background:#fff;color:#a79d95;content:'\e616';font-size:2.2rem}.admin__actions-switch{display:inline-block;position:relative;vertical-align:middle}.admin__field-control .admin__actions-switch{line-height:3.2rem}.admin__actions-switch+.admin__field-service{min-width:34rem}._disabled .admin__actions-switch-checkbox+.admin__actions-switch-label,.admin__actions-switch-checkbox.disabled+.admin__actions-switch-label{cursor:not-allowed;opacity:.5;pointer-events:none}.admin__actions-switch-checkbox:checked+.admin__actions-switch-label:before{left:15px}.admin__actions-switch-checkbox:checked+.admin__actions-switch-label:after{background:#79a22e}.admin__actions-switch-checkbox:checked+.admin__actions-switch-label .admin__actions-switch-text:before{content:attr(data-text-on)}.admin__actions-switch-checkbox:focus+.admin__actions-switch-label:after,.admin__actions-switch-checkbox:focus+.admin__actions-switch-label:before{border-color:#007bdb}._error .admin__actions-switch-checkbox+.admin__actions-switch-label:after,._error .admin__actions-switch-checkbox+.admin__actions-switch-label:before{border-color:#e22626}.admin__actions-switch-label{cursor:pointer;display:inline-block;height:22px;line-height:22px;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle}.admin__actions-switch-label:after,.admin__actions-switch-label:before{left:0;position:absolute;right:auto;top:0}.admin__actions-switch-label:before{background:#fff;border:1px solid #aaa6a0;border-radius:100%;content:'';display:block;height:22px;transition:left .2s ease-in 0s;width:22px;z-index:1}.admin__actions-switch-label:after{background:#e3e3e3;border:1px solid #aaa6a0;border-radius:12px;content:'';display:block;height:22px;transition:background .2s ease-in 0s;vertical-align:middle;width:37px;z-index:0}.admin__actions-switch-text:before{content:attr(data-text-off);padding-left:47px;white-space:nowrap}.abs-action-delete,.abs-action-reset,.action-close,.admin__field-fallback-reset,.extensions-information .list .extension-delete,.notifications-close,.search-global-field._active .search-global-action{background-color:transparent;border:none;border-radius:0;box-shadow:none;margin:0;padding:0}.abs-action-delete:hover,.abs-action-reset:hover,.action-close:hover,.admin__field-fallback-reset:hover,.extensions-information .list .extension-delete:hover,.notifications-close:hover,.search-global-field._active .search-global-action:hover{background-color:transparent;border:none;box-shadow:none}.abs-action-default,.abs-action-pattern,.abs-action-primary,.abs-action-quaternary,.abs-action-secondary,.abs-action-tertiary,.action-default,.action-primary,.action-quaternary,.action-secondary,.action-tertiary,.modal-popup .modal-footer .action-primary,.modal-popup .modal-footer .action-secondary,.page-actions .page-actions-buttons>button,.page-actions .page-actions-buttons>button.action-primary,.page-actions .page-actions-buttons>button.action-secondary,.page-actions .page-actions-buttons>button.primary,.page-actions>button,.page-actions>button.action-primary,.page-actions>button.action-secondary,.page-actions>button.primary,button,button.primary,button.secondary,button.tertiary{border:1px solid;border-radius:0;display:inline-block;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.4rem;font-weight:600;line-height:1.36;padding:.6rem 1em;text-align:center;vertical-align:baseline}.abs-action-default.disabled,.abs-action-default[disabled],.abs-action-pattern.disabled,.abs-action-pattern[disabled],.abs-action-primary.disabled,.abs-action-primary[disabled],.abs-action-quaternary.disabled,.abs-action-quaternary[disabled],.abs-action-secondary.disabled,.abs-action-secondary[disabled],.abs-action-tertiary.disabled,.abs-action-tertiary[disabled],.action-default.disabled,.action-default[disabled],.action-primary.disabled,.action-primary[disabled],.action-quaternary.disabled,.action-quaternary[disabled],.action-secondary.disabled,.action-secondary[disabled],.action-tertiary.disabled,.action-tertiary[disabled],.modal-popup .modal-footer .action-primary.disabled,.modal-popup .modal-footer .action-primary[disabled],.modal-popup .modal-footer .action-secondary.disabled,.modal-popup .modal-footer .action-secondary[disabled],.page-actions .page-actions-buttons>button.action-primary.disabled,.page-actions .page-actions-buttons>button.action-primary[disabled],.page-actions .page-actions-buttons>button.action-secondary.disabled,.page-actions .page-actions-buttons>button.action-secondary[disabled],.page-actions .page-actions-buttons>button.disabled,.page-actions .page-actions-buttons>button.primary.disabled,.page-actions .page-actions-buttons>button.primary[disabled],.page-actions .page-actions-buttons>button[disabled],.page-actions>button.action-primary.disabled,.page-actions>button.action-primary[disabled],.page-actions>button.action-secondary.disabled,.page-actions>button.action-secondary[disabled],.page-actions>button.disabled,.page-actions>button.primary.disabled,.page-actions>button.primary[disabled],.page-actions>button[disabled],button.disabled,button.primary.disabled,button.primary[disabled],button.secondary.disabled,button.secondary[disabled],button.tertiary.disabled,button.tertiary[disabled],button[disabled]{cursor:default;opacity:.5;pointer-events:none}.abs-action-l,.modal-popup .modal-footer .action-primary,.modal-popup .modal-footer .action-secondary,.page-actions .page-actions-buttons>button,.page-actions .page-actions-buttons>button.action-primary,.page-actions .page-actions-buttons>button.action-secondary,.page-actions .page-actions-buttons>button.primary,.page-actions button,.page-actions>button.action-primary,.page-actions>button.action-secondary,.page-actions>button.primary{font-size:1.6rem;letter-spacing:.025em;padding-bottom:.6875em;padding-top:.6875em}.abs-action-delete,.extensions-information .list .extension-delete{display:inline-block;font-size:1.6rem;margin-left:1.2rem;padding-top:.7rem;text-decoration:none;vertical-align:middle}.abs-action-delete:after,.extensions-information .list .extension-delete:after{color:#666;content:'\e630'}.abs-action-delete:hover:after,.extensions-information .list .extension-delete:hover:after{color:#35302c}.abs-action-button-as-link,.action-advanced,.data-grid .action-delete{line-height:1.36;padding:0;color:#008bdb;text-decoration:none;background:0 0;border:0;display:inline;font-weight:400;border-radius:0}.abs-action-button-as-link:visited,.action-advanced:visited,.data-grid .action-delete:visited{color:#008bdb;text-decoration:none}.abs-action-button-as-link:hover,.action-advanced:hover,.data-grid .action-delete:hover{text-decoration:underline}.abs-action-button-as-link:active,.action-advanced:active,.data-grid .action-delete:active{color:#ff5501;text-decoration:underline}.abs-action-button-as-link:hover,.action-advanced:hover,.data-grid .action-delete:hover{color:#0fa7ff}.abs-action-button-as-link:active,.abs-action-button-as-link:focus,.abs-action-button-as-link:hover,.action-advanced:active,.action-advanced:focus,.action-advanced:hover,.data-grid .action-delete:active,.data-grid .action-delete:focus,.data-grid .action-delete:hover{background:0 0;border:0}.abs-action-button-as-link.disabled,.abs-action-button-as-link[disabled],.action-advanced.disabled,.action-advanced[disabled],.data-grid .action-delete.disabled,.data-grid .action-delete[disabled],fieldset[disabled] .abs-action-button-as-link,fieldset[disabled] .action-advanced,fieldset[disabled] .data-grid .action-delete{color:#008bdb;opacity:.5;cursor:default;pointer-events:none;text-decoration:underline}.abs-action-button-as-link:active,.abs-action-button-as-link:not(:focus),.action-advanced:active,.action-advanced:not(:focus),.data-grid .action-delete:active,.data-grid .action-delete:not(:focus){box-shadow:none}.abs-action-button-as-link:focus,.action-advanced:focus,.data-grid .action-delete:focus{color:#0fa7ff}.abs-action-default,button{background:#e3e3e3;border-color:#adadad;color:#514943}.abs-action-default:active,.abs-action-default:focus,.abs-action-default:hover,button:active,button:focus,button:hover{background-color:#dbdbdb;color:#514943;text-decoration:none}.abs-action-primary,.page-actions .page-actions-buttons>button.action-primary,.page-actions .page-actions-buttons>button.primary,.page-actions>button.action-primary,.page-actions>button.primary,button.primary{background-color:#eb5202;border-color:#eb5202;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.25)}.abs-action-primary:active,.abs-action-primary:focus,.abs-action-primary:hover,.page-actions .page-actions-buttons>button.action-primary:active,.page-actions .page-actions-buttons>button.action-primary:focus,.page-actions .page-actions-buttons>button.action-primary:hover,.page-actions .page-actions-buttons>button.primary:active,.page-actions .page-actions-buttons>button.primary:focus,.page-actions .page-actions-buttons>button.primary:hover,.page-actions>button.action-primary:active,.page-actions>button.action-primary:focus,.page-actions>button.action-primary:hover,.page-actions>button.primary:active,.page-actions>button.primary:focus,.page-actions>button.primary:hover,button.primary:active,button.primary:focus,button.primary:hover{background-color:#ba4000;border-color:#b84002;box-shadow:0 0 0 1px #007bdb;color:#fff;text-decoration:none}.abs-action-primary.disabled,.abs-action-primary[disabled],.page-actions .page-actions-buttons>button.action-primary.disabled,.page-actions .page-actions-buttons>button.action-primary[disabled],.page-actions .page-actions-buttons>button.primary.disabled,.page-actions .page-actions-buttons>button.primary[disabled],.page-actions>button.action-primary.disabled,.page-actions>button.action-primary[disabled],.page-actions>button.primary.disabled,.page-actions>button.primary[disabled],button.primary.disabled,button.primary[disabled]{cursor:default;opacity:.5;pointer-events:none}.abs-action-secondary,.modal-popup .modal-footer .action-primary,.page-actions .page-actions-buttons>button.action-secondary,.page-actions>button.action-secondary,button.secondary{background-color:#514943;border-color:#514943;color:#fff;text-shadow:1px 1px 1px rgba(0,0,0,.3)}.abs-action-secondary:active,.abs-action-secondary:focus,.abs-action-secondary:hover,.modal-popup .modal-footer .action-primary:active,.modal-popup .modal-footer .action-primary:focus,.modal-popup .modal-footer .action-primary:hover,.page-actions .page-actions-buttons>button.action-secondary:active,.page-actions .page-actions-buttons>button.action-secondary:focus,.page-actions .page-actions-buttons>button.action-secondary:hover,.page-actions>button.action-secondary:active,.page-actions>button.action-secondary:focus,.page-actions>button.action-secondary:hover,button.secondary:active,button.secondary:focus,button.secondary:hover{background-color:#35302c;border-color:#35302c;box-shadow:0 0 0 1px #007bdb;color:#fff;text-decoration:none}.abs-action-secondary:active,.modal-popup .modal-footer .action-primary:active,.page-actions .page-actions-buttons>button.action-secondary:active,.page-actions>button.action-secondary:active,button.secondary:active{background-color:#35302c}.abs-action-tertiary,.modal-popup .modal-footer .action-secondary,button.tertiary{background-color:transparent;border-color:transparent;text-shadow:none;color:#008bdb}.abs-action-tertiary:active,.abs-action-tertiary:focus,.abs-action-tertiary:hover,.modal-popup .modal-footer .action-secondary:active,.modal-popup .modal-footer .action-secondary:focus,.modal-popup .modal-footer .action-secondary:hover,button.tertiary:active,button.tertiary:focus,button.tertiary:hover{background-color:transparent;border-color:transparent;box-shadow:none;color:#0fa7ff;text-decoration:underline}.abs-action-quaternary,.page-actions .page-actions-buttons>button,.page-actions>button{background-color:transparent;border-color:transparent;text-shadow:none;color:#333}.abs-action-quaternary:active,.abs-action-quaternary:focus,.abs-action-quaternary:hover,.page-actions .page-actions-buttons>button:active,.page-actions .page-actions-buttons>button:focus,.page-actions .page-actions-buttons>button:hover,.page-actions>button:active,.page-actions>button:focus,.page-actions>button:hover{background-color:transparent;border-color:transparent;box-shadow:none;color:#1a1a1a}.abs-action-menu,.actions-split .abs-action-menu .action-submenu,.actions-split .abs-action-menu .action-submenu .action-submenu,.actions-split .action-menu,.actions-split .action-menu .action-submenu,.actions-split .actions-split .dropdown-menu .action-submenu,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu,.actions-split .dropdown-menu{text-align:left;background-color:#fff;border:1px solid #007bdb;border-radius:1px;box-shadow:1px 1px 5px rgba(0,0,0,.5);color:#333;display:none;font-weight:400;left:0;list-style:none;margin:2px 0 0;min-width:0;padding:0;position:absolute;right:0;top:100%}.abs-action-menu._active,.actions-split .abs-action-menu .action-submenu .action-submenu._active,.actions-split .abs-action-menu .action-submenu._active,.actions-split .action-menu .action-submenu._active,.actions-split .action-menu._active,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu._active,.actions-split .actions-split .dropdown-menu .action-submenu._active,.actions-split .dropdown-menu._active{display:block}.abs-action-menu>li,.actions-split .abs-action-menu .action-submenu .action-submenu>li,.actions-split .abs-action-menu .action-submenu>li,.actions-split .action-menu .action-submenu>li,.actions-split .action-menu>li,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li,.actions-split .actions-split .dropdown-menu .action-submenu>li,.actions-split .dropdown-menu>li{border:none;display:block;padding:0;transition:background-color .1s linear}.abs-action-menu>li>a:hover,.actions-split .abs-action-menu .action-submenu .action-submenu>li>a:hover,.actions-split .abs-action-menu .action-submenu>li>a:hover,.actions-split .action-menu .action-submenu>li>a:hover,.actions-split .action-menu>li>a:hover,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li>a:hover,.actions-split .actions-split .dropdown-menu .action-submenu>li>a:hover,.actions-split .dropdown-menu>li>a:hover{text-decoration:none}.abs-action-menu>li._visible,.abs-action-menu>li:hover,.actions-split .abs-action-menu .action-submenu .action-submenu>li._visible,.actions-split .abs-action-menu .action-submenu .action-submenu>li:hover,.actions-split .abs-action-menu .action-submenu>li._visible,.actions-split .abs-action-menu .action-submenu>li:hover,.actions-split .action-menu .action-submenu>li._visible,.actions-split .action-menu .action-submenu>li:hover,.actions-split .action-menu>li._visible,.actions-split .action-menu>li:hover,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li._visible,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li:hover,.actions-split .actions-split .dropdown-menu .action-submenu>li._visible,.actions-split .actions-split .dropdown-menu .action-submenu>li:hover,.actions-split .dropdown-menu>li._visible,.actions-split .dropdown-menu>li:hover{background-color:#e3e3e3}.abs-action-menu>li:active,.actions-split .abs-action-menu .action-submenu .action-submenu>li:active,.actions-split .abs-action-menu .action-submenu>li:active,.actions-split .action-menu .action-submenu>li:active,.actions-split .action-menu>li:active,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li:active,.actions-split .actions-split .dropdown-menu .action-submenu>li:active,.actions-split .dropdown-menu>li:active{background-color:#cacaca}.abs-action-menu>li._parent,.actions-split .abs-action-menu .action-submenu .action-submenu>li._parent,.actions-split .abs-action-menu .action-submenu>li._parent,.actions-split .action-menu .action-submenu>li._parent,.actions-split .action-menu>li._parent,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li._parent,.actions-split .actions-split .dropdown-menu .action-submenu>li._parent,.actions-split .dropdown-menu>li._parent{-webkit-flex-direction:row;display:flex;-ms-flex-direction:row;flex-direction:row}.abs-action-menu>li._parent>.action-menu-item,.actions-split .abs-action-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.actions-split .abs-action-menu .action-submenu>li._parent>.action-menu-item,.actions-split .action-menu .action-submenu>li._parent>.action-menu-item,.actions-split .action-menu>li._parent>.action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu>li._parent>.action-menu-item,.actions-split .dropdown-menu>li._parent>.action-menu-item{min-width:100%}.abs-action-menu .action-menu-item,.abs-action-menu .item,.actions-split .abs-action-menu .action-submenu .action-menu-item,.actions-split .abs-action-menu .action-submenu .action-submenu .action-menu-item,.actions-split .abs-action-menu .action-submenu .action-submenu .item,.actions-split .abs-action-menu .action-submenu .item,.actions-split .action-menu .action-menu-item,.actions-split .action-menu .action-submenu .action-menu-item,.actions-split .action-menu .action-submenu .item,.actions-split .action-menu .item,.actions-split .actions-split .dropdown-menu .action-submenu .action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu .action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu .item,.actions-split .actions-split .dropdown-menu .action-submenu .item,.actions-split .dropdown-menu .action-menu-item,.actions-split .dropdown-menu .item{cursor:pointer;display:block;padding:.6875em 1em}.abs-action-menu .action-submenu,.actions-split .action-menu .action-submenu,.actions-split .action-menu .action-submenu .action-submenu,.actions-split .dropdown-menu .action-submenu{bottom:auto;left:auto;margin-left:0;margin-top:-1px;position:absolute;right:auto;top:auto}.ie9 .abs-action-menu .action-submenu,.ie9 .actions-split .abs-action-menu .action-submenu .action-submenu,.ie9 .actions-split .abs-action-menu .action-submenu .action-submenu .action-submenu,.ie9 .actions-split .action-menu .action-submenu,.ie9 .actions-split .action-menu .action-submenu .action-submenu,.ie9 .actions-split .actions-split .dropdown-menu .action-submenu .action-submenu,.ie9 .actions-split .actions-split .dropdown-menu .action-submenu .action-submenu .action-submenu,.ie9 .actions-split .dropdown-menu .action-submenu{margin-left:99%;margin-top:-3.5rem}.abs-action-menu a.action-menu-item,.actions-split .abs-action-menu .action-submenu .action-submenu a.action-menu-item,.actions-split .abs-action-menu .action-submenu a.action-menu-item,.actions-split .action-menu .action-submenu a.action-menu-item,.actions-split .action-menu a.action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu a.action-menu-item,.actions-split .actions-split .dropdown-menu .action-submenu a.action-menu-item,.actions-split .dropdown-menu a.action-menu-item{color:#333}.abs-action-menu a.action-menu-item:focus,.actions-split .abs-action-menu .action-submenu .action-submenu a.action-menu-item:focus,.actions-split .abs-action-menu .action-submenu a.action-menu-item:focus,.actions-split .action-menu .action-submenu a.action-menu-item:focus,.actions-split .action-menu a.action-menu-item:focus,.actions-split .actions-split .dropdown-menu .action-submenu .action-submenu a.action-menu-item:focus,.actions-split .actions-split .dropdown-menu .action-submenu a.action-menu-item:focus,.actions-split .dropdown-menu a.action-menu-item:focus{background-color:#e3e3e3;box-shadow:none}.abs-action-wrap-triangle{position:relative}.abs-action-wrap-triangle .action-default{width:100%}.abs-action-wrap-triangle .action-default:after,.abs-action-wrap-triangle .action-default:before{border-style:solid;content:'';height:0;position:absolute;top:0;width:0}.abs-action-wrap-triangle .action-default:active,.abs-action-wrap-triangle .action-default:focus,.abs-action-wrap-triangle .action-default:hover{box-shadow:none}._keyfocus .abs-action-wrap-triangle .action-default:focus{box-shadow:0 0 0 1px #007bdb}.ie10 .abs-action-wrap-triangle .action-default.disabled,.ie10 .abs-action-wrap-triangle .action-default[disabled],.ie9 .abs-action-wrap-triangle .action-default.disabled,.ie9 .abs-action-wrap-triangle .action-default[disabled]{background-color:#fcfcfc;opacity:1;text-shadow:none}.abs-action-wrap-triangle-right{display:inline-block;padding-right:1.6rem;position:relative}.abs-action-wrap-triangle-right .action-default:after,.abs-action-wrap-triangle-right .action-default:before{border-color:transparent transparent transparent #e3e3e3;border-width:1.7rem 0 1.6rem 1.7rem;left:100%;margin-left:-1.7rem}.abs-action-wrap-triangle-right .action-default:before{border-left-color:#949494;right:-1px}.abs-action-wrap-triangle-right .action-default:active:after,.abs-action-wrap-triangle-right .action-default:focus:after,.abs-action-wrap-triangle-right .action-default:hover:after{border-left-color:#dbdbdb}.ie10 .abs-action-wrap-triangle-right .action-default.disabled:after,.ie10 .abs-action-wrap-triangle-right .action-default[disabled]:after,.ie9 .abs-action-wrap-triangle-right .action-default.disabled:after,.ie9 .abs-action-wrap-triangle-right .action-default[disabled]:after{border-color:transparent transparent transparent #fcfcfc}.abs-action-wrap-triangle-right .action-primary:after{border-color:transparent transparent transparent #eb5202}.abs-action-wrap-triangle-right .action-primary:active:after,.abs-action-wrap-triangle-right .action-primary:focus:after,.abs-action-wrap-triangle-right .action-primary:hover:after{border-left-color:#ba4000}.abs-action-wrap-triangle-left{display:inline-block;padding-left:1.6rem}.abs-action-wrap-triangle-left .action-default{text-indent:-.85rem}.abs-action-wrap-triangle-left .action-default:after,.abs-action-wrap-triangle-left .action-default:before{border-color:transparent #e3e3e3 transparent transparent;border-width:1.7rem 1.7rem 1.6rem 0;margin-right:-1.7rem;right:100%}.abs-action-wrap-triangle-left .action-default:before{border-right-color:#949494;left:-1px}.abs-action-wrap-triangle-left .action-default:active:after,.abs-action-wrap-triangle-left .action-default:focus:after,.abs-action-wrap-triangle-left .action-default:hover:after{border-right-color:#dbdbdb}.ie10 .abs-action-wrap-triangle-left .action-default.disabled:after,.ie10 .abs-action-wrap-triangle-left .action-default[disabled]:after,.ie9 .abs-action-wrap-triangle-left .action-default.disabled:after,.ie9 .abs-action-wrap-triangle-left .action-default[disabled]:after{border-color:transparent #fcfcfc transparent transparent}.abs-action-wrap-triangle-left .action-primary:after{border-color:transparent #eb5202 transparent transparent}.abs-action-wrap-triangle-left .action-primary:active:after,.abs-action-wrap-triangle-left .action-primary:focus:after,.abs-action-wrap-triangle-left .action-primary:hover:after{border-right-color:#ba4000}.action-default,button{background:#e3e3e3;border-color:#adadad;color:#514943}.action-default:active,.action-default:focus,.action-default:hover,button:active,button:focus,button:hover{background-color:#dbdbdb;color:#514943;text-decoration:none}.action-primary{background-color:#eb5202;border-color:#eb5202;color:#fff;text-shadow:1px 1px 0 rgba(0,0,0,.25)}.action-primary:active,.action-primary:focus,.action-primary:hover{background-color:#ba4000;border-color:#b84002;box-shadow:0 0 0 1px #007bdb;color:#fff;text-decoration:none}.action-primary.disabled,.action-primary[disabled]{cursor:default;opacity:.5;pointer-events:none}.action-secondary{background-color:#514943;border-color:#514943;color:#fff;text-shadow:1px 1px 1px rgba(0,0,0,.3)}.action-secondary:active,.action-secondary:focus,.action-secondary:hover{background-color:#35302c;border-color:#35302c;box-shadow:0 0 0 1px #007bdb;color:#fff;text-decoration:none}.action-secondary:active{background-color:#35302c}.action-quaternary,.action-tertiary{background-color:transparent;border-color:transparent;text-shadow:none}.action-quaternary:active,.action-quaternary:focus,.action-quaternary:hover,.action-tertiary:active,.action-tertiary:focus,.action-tertiary:hover{background-color:transparent;border-color:transparent;box-shadow:none}.action-tertiary{color:#008bdb}.action-tertiary:active,.action-tertiary:focus,.action-tertiary:hover{color:#0fa7ff;text-decoration:underline}.action-quaternary{color:#333}.action-quaternary:active,.action-quaternary:focus,.action-quaternary:hover{color:#1a1a1a}.action-close>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.action-close:active{-ms-transform:scale(0.9);transform:scale(0.9)}.action-close:before{content:'\e62f';transition:color .1s linear}.action-close:hover{cursor:pointer;text-decoration:none}.abs-action-menu .action-submenu,.abs-action-menu .action-submenu .action-submenu,.action-menu,.action-menu .action-submenu,.actions-split .action-menu .action-submenu,.actions-split .action-menu .action-submenu .action-submenu,.actions-split .dropdown-menu .action-submenu,.actions-split .dropdown-menu .action-submenu .action-submenu{background-color:#fff;border:1px solid #007bdb;border-radius:1px;box-shadow:1px 1px 5px rgba(0,0,0,.5);color:#333;display:none;font-weight:400;left:0;list-style:none;margin:2px 0 0;min-width:0;padding:0;position:absolute;right:0;top:100%}.abs-action-menu .action-submenu .action-submenu._active,.abs-action-menu .action-submenu._active,.action-menu .action-submenu._active,.action-menu._active,.actions-split .action-menu .action-submenu .action-submenu._active,.actions-split .action-menu .action-submenu._active,.actions-split .dropdown-menu .action-submenu .action-submenu._active,.actions-split .dropdown-menu .action-submenu._active{display:block}.abs-action-menu .action-submenu .action-submenu>li,.abs-action-menu .action-submenu>li,.action-menu .action-submenu>li,.action-menu>li,.actions-split .action-menu .action-submenu .action-submenu>li,.actions-split .action-menu .action-submenu>li,.actions-split .dropdown-menu .action-submenu .action-submenu>li,.actions-split .dropdown-menu .action-submenu>li{border:none;display:block;padding:0;transition:background-color .1s linear}.abs-action-menu .action-submenu .action-submenu>li>a:hover,.abs-action-menu .action-submenu>li>a:hover,.action-menu .action-submenu>li>a:hover,.action-menu>li>a:hover,.actions-split .action-menu .action-submenu .action-submenu>li>a:hover,.actions-split .action-menu .action-submenu>li>a:hover,.actions-split .dropdown-menu .action-submenu .action-submenu>li>a:hover,.actions-split .dropdown-menu .action-submenu>li>a:hover{text-decoration:none}.abs-action-menu .action-submenu .action-submenu>li._visible,.abs-action-menu .action-submenu .action-submenu>li:hover,.abs-action-menu .action-submenu>li._visible,.abs-action-menu .action-submenu>li:hover,.action-menu .action-submenu>li._visible,.action-menu .action-submenu>li:hover,.action-menu>li._visible,.action-menu>li:hover,.actions-split .action-menu .action-submenu .action-submenu>li._visible,.actions-split .action-menu .action-submenu .action-submenu>li:hover,.actions-split .action-menu .action-submenu>li._visible,.actions-split .action-menu .action-submenu>li:hover,.actions-split .dropdown-menu .action-submenu .action-submenu>li._visible,.actions-split .dropdown-menu .action-submenu .action-submenu>li:hover,.actions-split .dropdown-menu .action-submenu>li._visible,.actions-split .dropdown-menu .action-submenu>li:hover{background-color:#e3e3e3}.abs-action-menu .action-submenu .action-submenu>li:active,.abs-action-menu .action-submenu>li:active,.action-menu .action-submenu>li:active,.action-menu>li:active,.actions-split .action-menu .action-submenu .action-submenu>li:active,.actions-split .action-menu .action-submenu>li:active,.actions-split .dropdown-menu .action-submenu .action-submenu>li:active,.actions-split .dropdown-menu .action-submenu>li:active{background-color:#cacaca}.abs-action-menu .action-submenu .action-submenu>li._parent,.abs-action-menu .action-submenu>li._parent,.action-menu .action-submenu>li._parent,.action-menu>li._parent,.actions-split .action-menu .action-submenu .action-submenu>li._parent,.actions-split .action-menu .action-submenu>li._parent,.actions-split .dropdown-menu .action-submenu .action-submenu>li._parent,.actions-split .dropdown-menu .action-submenu>li._parent{-webkit-flex-direction:row;display:flex;-ms-flex-direction:row;flex-direction:row}.abs-action-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.abs-action-menu .action-submenu>li._parent>.action-menu-item,.action-menu .action-submenu>li._parent>.action-menu-item,.action-menu>li._parent>.action-menu-item,.actions-split .action-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.actions-split .action-menu .action-submenu>li._parent>.action-menu-item,.actions-split .dropdown-menu .action-submenu .action-submenu>li._parent>.action-menu-item,.actions-split .dropdown-menu .action-submenu>li._parent>.action-menu-item{min-width:100%}.abs-action-menu .action-submenu .action-menu-item,.abs-action-menu .action-submenu .action-submenu .action-menu-item,.abs-action-menu .action-submenu .action-submenu .item,.abs-action-menu .action-submenu .item,.action-menu .action-menu-item,.action-menu .action-submenu .action-menu-item,.action-menu .action-submenu .item,.action-menu .item,.actions-split .action-menu .action-submenu .action-menu-item,.actions-split .action-menu .action-submenu .action-submenu .action-menu-item,.actions-split .action-menu .action-submenu .action-submenu .item,.actions-split .action-menu .action-submenu .item,.actions-split .dropdown-menu .action-submenu .action-menu-item,.actions-split .dropdown-menu .action-submenu .action-submenu .action-menu-item,.actions-split .dropdown-menu .action-submenu .action-submenu .item,.actions-split .dropdown-menu .action-submenu .item{cursor:pointer;display:block;padding:.6875em 1em}.abs-action-menu .action-submenu .action-submenu,.action-menu .action-submenu,.actions-split .action-menu .action-submenu .action-submenu,.actions-split .dropdown-menu .action-submenu .action-submenu{bottom:auto;left:auto;margin-left:0;margin-top:-1px;position:absolute;right:auto;top:auto}.ie9 .abs-action-menu .action-submenu .action-submenu,.ie9 .abs-action-menu .action-submenu .action-submenu .action-submenu,.ie9 .action-menu .action-submenu,.ie9 .action-menu .action-submenu .action-submenu,.ie9 .actions-split .action-menu .action-submenu .action-submenu,.ie9 .actions-split .action-menu .action-submenu .action-submenu .action-submenu,.ie9 .actions-split .dropdown-menu .action-submenu .action-submenu,.ie9 .actions-split .dropdown-menu .action-submenu .action-submenu .action-submenu{margin-left:99%;margin-top:-3.5rem}.abs-action-menu .action-submenu .action-submenu a.action-menu-item,.abs-action-menu .action-submenu a.action-menu-item,.action-menu .action-submenu a.action-menu-item,.action-menu a.action-menu-item,.actions-split .action-menu .action-submenu .action-submenu a.action-menu-item,.actions-split .action-menu .action-submenu a.action-menu-item,.actions-split .dropdown-menu .action-submenu .action-submenu a.action-menu-item,.actions-split .dropdown-menu .action-submenu a.action-menu-item{color:#333}.abs-action-menu .action-submenu .action-submenu a.action-menu-item:focus,.abs-action-menu .action-submenu a.action-menu-item:focus,.action-menu .action-submenu a.action-menu-item:focus,.action-menu a.action-menu-item:focus,.actions-split .action-menu .action-submenu .action-submenu a.action-menu-item:focus,.actions-split .action-menu .action-submenu a.action-menu-item:focus,.actions-split .dropdown-menu .action-submenu .action-submenu a.action-menu-item:focus,.actions-split .dropdown-menu .action-submenu a.action-menu-item:focus{background-color:#e3e3e3;box-shadow:none}.messages .message:last-child{margin:0 0 2rem}.message{background:#fffbbb;border:none;border-radius:0;color:#333;font-size:1.4rem;margin:0 0 1px;padding:1.8rem 4rem 1.8rem 5.5rem;position:relative;text-shadow:none}.message:before{background:0 0;border:0;color:#007bdb;content:'\e61a';font-family:Icons;font-size:1.9rem;font-style:normal;font-weight:400;height:auto;left:1.9rem;line-height:inherit;margin-top:-1.3rem;position:absolute;speak:none;text-shadow:none;top:50%;width:auto}.message-notice:before{color:#007bdb;content:'\e61a'}.message-warning:before{color:#eb5202;content:'\e623'}.message-error{background:#fcc}.message-error:before{color:#e22626;content:'\e632';font-size:1.5rem;left:2.2rem;margin-top:-1rem}.message-success:before{color:#79a22e;content:'\e62d'}.message-spinner:before{display:none}.message-spinner .spinner{font-size:2.5rem;left:1.5rem;position:absolute;top:1.5rem}.message-in-rating-edit{margin-left:1.8rem;margin-right:1.8rem}.modal-popup .action-close,.modal-slide .action-close{color:#736963;position:absolute;right:0;top:0;z-index:1}.modal-popup .action-close:active,.modal-slide .action-close:active{-ms-transform:none;transform:none}.modal-popup .action-close:active:before,.modal-slide .action-close:active:before{font-size:1.8rem}.modal-popup .action-close:hover:before,.modal-slide .action-close:hover:before{color:#58504b}.modal-popup .action-close:before,.modal-slide .action-close:before{font-size:2rem}.modal-popup .action-close:focus,.modal-slide .action-close:focus{background-color:transparent}.modal-popup.prompt .prompt-message{padding:2rem 0}.modal-popup.prompt .prompt-message input{width:100%}.modal-popup.confirm .modal-inner-wrap .message,.modal-popup.prompt .modal-inner-wrap .message{background:#fff}.modal-popup.modal-system-messages .modal-inner-wrap{background:#fffbbb}.modal-popup._image-box .modal-inner-wrap{margin:5rem auto;max-width:78rem;position:static}.modal-popup._image-box .thumbnail-preview{padding-bottom:3rem;text-align:center}.modal-popup._image-box .thumbnail-preview .thumbnail-preview-image-block{border:1px solid #ccc;margin:0 auto 2rem;max-width:58rem;padding:2rem}.modal-popup._image-box .thumbnail-preview .thumbnail-preview-image{max-height:54rem}.modal-popup .modal-title{font-size:2.4rem;margin-right:6.4rem}.modal-popup .modal-footer{padding-top:2.6rem;text-align:right}.modal-popup .action-close{padding:3rem}.modal-popup .action-close:active,.modal-popup .action-close:focus{background:0 0;padding-right:3.1rem;padding-top:3.1rem}.modal-slide .modal-content-new-attribute{-webkit-overflow-scrolling:touch;overflow:auto;padding-bottom:0}.modal-slide .modal-content-new-attribute iframe{margin-bottom:-2.5rem}.modal-slide .modal-title{font-size:2.1rem;margin-right:5.7rem}.modal-slide .action-close{padding:2.1rem 2.6rem}.modal-slide .action-close:active{padding-right:2.7rem;padding-top:2.2rem}.modal-slide .page-main-actions{margin-bottom:.6rem;margin-top:2.1rem}.modal-slide .magento-message{padding:0 3rem 3rem;position:relative}.modal-slide .magento-message .insert-title-inner,.modal-slide .main-col .insert-title-inner{border-bottom:1px solid #adadad;margin:0 0 2rem;padding-bottom:.5rem}.modal-slide .magento-message .insert-actions,.modal-slide .main-col .insert-actions{float:right}.modal-slide .magento-message .title,.modal-slide .main-col .title{font-size:1.6rem;padding-top:.5rem}.modal-slide .main-col,.modal-slide .side-col{float:left;padding-bottom:0}.modal-slide .main-col:after,.modal-slide .side-col:after{display:none}.modal-slide .side-col{width:20%}.modal-slide .main-col{padding-right:0;width:80%}.modal-slide .content-footer .form-buttons{float:right}.modal-title{font-weight:400;margin-bottom:0;min-height:1em}.modal-title span{font-size:1.4rem;font-style:italic;margin-left:1rem}.spinner{display:inline-block;font-size:4rem;height:1em;margin-right:1.5rem;position:relative;width:1em}.spinner>span:nth-child(1){animation-delay:.27s;-ms-transform:rotate(-315deg);transform:rotate(-315deg)}.spinner>span:nth-child(2){animation-delay:.36s;-ms-transform:rotate(-270deg);transform:rotate(-270deg)}.spinner>span:nth-child(3){animation-delay:.45s;-ms-transform:rotate(-225deg);transform:rotate(-225deg)}.spinner>span:nth-child(4){animation-delay:.54s;-ms-transform:rotate(-180deg);transform:rotate(-180deg)}.spinner>span:nth-child(5){animation-delay:.63s;-ms-transform:rotate(-135deg);transform:rotate(-135deg)}.spinner>span:nth-child(6){animation-delay:.72s;-ms-transform:rotate(-90deg);transform:rotate(-90deg)}.spinner>span:nth-child(7){animation-delay:.81s;-ms-transform:rotate(-45deg);transform:rotate(-45deg)}.spinner>span:nth-child(8){animation-delay:.9;-ms-transform:rotate(0deg);transform:rotate(0deg)}@keyframes fade{0%{background-color:#514943}100%{background-color:#fff}}.spinner>span{-ms-transform:scale(0.4);transform:scale(0.4);animation-name:fade;animation-duration:.72s;animation-iteration-count:infinite;animation-direction:linear;background-color:#fff;border-radius:6px;clip:rect(0 .28571429em .1em 0);height:.1em;margin-top:.5em;position:absolute;width:1em}.ie9 .spinner{background:url(../images/ajax-loader.gif) center no-repeat}.ie9 .spinner>span{display:none}.popup-loading{background:rgba(255,255,255,.8);border-color:#ef672f;color:#ef672f;font-size:14px;font-weight:700;left:50%;margin-left:-100px;padding:100px 0 10px;position:fixed;text-align:center;top:40%;width:200px;z-index:1003}.popup-loading:after{background-image:url(../images/loader-1.gif);content:'';height:64px;left:50%;margin:-32px 0 0 -32px;position:absolute;top:40%;width:64px;z-index:2}.loading-mask,.loading-old{background:rgba(255,255,255,.4);bottom:0;left:0;position:fixed;right:0;top:0;z-index:2003}.loading-mask img,.loading-old img{display:none}.loading-mask p,.loading-old p{margin-top:118px}.loading-mask .loader,.loading-old .loader{background:url(../images/loader-1.gif) 50% 30% no-repeat #f7f3eb;border-radius:5px;bottom:0;color:#575757;font-size:14px;font-weight:700;height:160px;left:0;margin:auto;opacity:.95;position:absolute;right:0;text-align:center;top:0;width:160px}.admin-user{float:right;line-height:1.36;margin-left:.3rem;z-index:490}.admin-user._active .admin__action-dropdown,.admin-user.active .admin__action-dropdown{border-color:#007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5)}.admin-user .admin__action-dropdown{height:3.3rem;padding:.7rem 2.8rem .4rem 4rem}.admin-user .admin__action-dropdown._active:after,.admin-user .admin__action-dropdown.active:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.admin-user .admin__action-dropdown:after{border-color:#777 transparent transparent;border-style:solid;border-width:.5rem .4rem 0;content:'';height:0;margin-top:-.2rem;position:absolute;right:1.3rem;top:50%;transition:all .2s linear;width:0}._active .admin-user .admin__action-dropdown:after,.active .admin-user .admin__action-dropdown:after{-ms-transform:rotate(180deg);transform:rotate(180deg)}.admin-user .admin__action-dropdown:hover:after{border-color:#000 transparent transparent}.admin-user .admin__action-dropdown:before{color:#777;content:'\e600';font-size:2rem;left:1.1rem;margin-top:-1.1rem;position:absolute;top:50%}.admin-user .admin__action-dropdown:hover:before{color:#333}.admin-user .admin__action-dropdown-menu{min-width:20rem;padding-left:1rem;padding-right:1rem}.admin-user .admin__action-dropdown-menu>li>a{padding-left:.5em;padding-right:1.8rem;transition:background-color .1s linear;white-space:nowrap}.admin-user .admin__action-dropdown-menu>li>a:hover{background-color:#e0f6fe;color:#333}.admin-user .admin__action-dropdown-menu>li>a:active{background-color:#c7effd;bottom:-1px;position:relative}.admin-user .admin__action-dropdown-menu .admin-user-name{text-overflow:ellipsis;white-space:nowrap;display:inline-block;max-width:20rem;overflow:hidden;vertical-align:top}.admin-user-account-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;max-width:11.2rem}.search-global{float:right;margin-right:-.3rem;position:relative;z-index:480}.search-global-field{min-width:5rem}.search-global-field._active .search-global-input{background-color:#fff;border-color:#007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5);padding-right:4rem;width:25rem}.search-global-field._active .search-global-action{display:block;height:3.3rem;position:absolute;right:0;text-indent:-100%;top:0;width:5rem;z-index:3}.search-global-field .autocomplete-results{height:3.3rem;position:absolute;right:0;top:0;width:25rem}.search-global-field .search-global-menu{border:1px solid #007bdb;border-top-color:transparent;box-shadow:1px 1px 5px rgba(0,0,0,.5);left:0;margin-top:-2px;padding:0;position:absolute;right:0;top:100%;z-index:2}.search-global-field .search-global-menu:after{background-color:#fff;content:'';height:5px;left:0;position:absolute;right:0;top:-5px}.search-global-field .search-global-menu>li{background-color:#fff;border-top:1px solid #ddd;display:block;font-size:1.2rem;padding:.75rem 1.4rem .55rem}.search-global-field .search-global-menu>li._active{background-color:#e0f6fe}.search-global-field .search-global-menu .title{display:block;font-size:1.4rem}.search-global-field .search-global-menu .type{color:#1a1a1a;display:block}.search-global-label{cursor:pointer;height:3.3rem;padding:.75rem 1.4rem .55rem;position:absolute;right:0;top:0;z-index:2}.search-global-label:active{-ms-transform:scale(0.9);transform:scale(0.9)}.search-global-label:hover:before{color:#000}.search-global-label:before{color:#777;content:'\e60c';font-size:2rem}.search-global-input{background-color:transparent;border:1px solid transparent;font-size:1.4rem;height:3.3rem;padding:.75rem 1.4rem .55rem;position:absolute;right:0;top:0;transition:all .1s linear,width .3s linear;width:5rem;z-index:1}.search-global-action{display:none}.notifications-wrapper{float:right;line-height:1;position:relative}.notifications-wrapper.active{z-index:500}.notifications-wrapper.active .notifications-action{border-color:#007bdb;box-shadow:1px 1px 5px rgba(0,0,0,.5)}.notifications-wrapper.active .notifications-action:after{background-color:#fff;border:none;content:'';display:block;height:6px;left:-6px;margin-top:0;position:absolute;right:0;top:100%;width:auto}.notifications-wrapper .admin__action-dropdown-menu{padding:1rem 0 0;width:32rem}.notifications-action{color:#777;height:3.3rem;padding:.75rem 2rem .65rem}.notifications-action:after{display:none}.notifications-action:before{content:'\e607';font-size:1.9rem;margin-right:0}.notifications-action:active:before{position:relative;top:1px}.notifications-action .notifications-counter{background-color:#e22626;border-radius:1em;color:#fff;display:inline-block;font-size:1.1rem;font-weight:700;left:50%;margin-left:.3em;margin-top:-1.1em;padding:.3em .5em;position:absolute;top:50%}.notifications-entry{line-height:1.36;padding:.6rem 2rem .8rem;position:relative;transition:background-color .1s linear}.notifications-entry:hover{background-color:#e0f6fe}.notifications-entry.notifications-entry-last{margin:0 2rem;padding:.3rem 0 1.3rem;text-align:center}.notifications-entry.notifications-entry-last:hover{background-color:transparent}.notifications-entry+.notifications-entry-last{border-top:1px solid #ddd;padding-bottom:.6rem}.notifications-entry ._cutted{cursor:pointer}.notifications-entry ._cutted .notifications-entry-description-start:after{content:'...'}.notifications-entry-title{color:#ef672f;display:block;font-size:1.1rem;font-weight:700;margin-bottom:.7rem;margin-right:1em}.notifications-entry-description{color:#333;font-size:1.1rem;margin-bottom:.8rem}.notifications-entry-description-end{display:none}.notifications-entry-description-end._show{display:inline}.notifications-entry-time{color:#777;font-size:1.1rem}.notifications-close{line-height:1;padding:1rem;position:absolute;right:0;top:.6rem}.notifications-close:before{color:#ccc;content:'\e620';transition:color .1s linear}.notifications-close:hover:before{color:#b3b3b3}.notifications-close:active{-ms-transform:scale(0.95);transform:scale(0.95)}.page-header-actions{padding-top:1.1rem}.page-header-hgroup{padding-right:1.5rem}.page-title{color:#333;font-size:2.8rem}.page-header{padding:1.5rem 3rem}.menu-wrapper{display:inline-block;position:relative;width:8.8rem;z-index:700}.menu-wrapper:before{background-color:#373330;bottom:0;content:'';left:0;position:fixed;top:0;width:8.8rem;z-index:699}.menu-wrapper._fixed{left:0;position:fixed;top:0}.menu-wrapper._fixed~.page-wrapper{margin-left:8.8rem}.menu-wrapper .logo{display:block;height:8.8rem;padding:2.4rem 0 2.2rem;position:relative;text-align:center;z-index:700}._keyfocus .menu-wrapper .logo:focus{background-color:#4a4542;box-shadow:none}._keyfocus .menu-wrapper .logo:focus+.admin__menu .level-0:first-child>a{background-color:#373330}._keyfocus .menu-wrapper .logo:focus+.admin__menu .level-0:first-child>a:after{display:none}.menu-wrapper .logo:hover .logo-img{-webkit-filter:brightness(1.1);filter:brightness(1.1)}.menu-wrapper .logo:active .logo-img{-ms-transform:scale(0.95);transform:scale(0.95)}.menu-wrapper .logo .logo-img{height:4.2rem;transition:-webkit-filter .2s linear,filter .2s linear,transform .1s linear;width:3.5rem}.abs-menu-separator,.admin__menu .item-partners>a:after,.admin__menu .level-0:first-child>a:after{background-color:#736963;content:'';display:block;height:1px;left:0;margin-left:16%;position:absolute;top:0;width:68%}.admin__menu li{display:block}.admin__menu .level-0:first-child>a{position:relative}.admin__menu .level-0._active>a,.admin__menu .level-0:hover>a{color:#f7f3eb}.admin__menu .level-0._active>a{background-color:#524d49}.admin__menu .level-0:hover>a{background-color:#4a4542}.admin__menu .level-0>a{color:#aaa6a0;display:block;font-size:1rem;letter-spacing:.025em;min-height:6.2rem;padding:1.2rem .5rem .5rem;position:relative;text-align:center;text-decoration:none;text-transform:uppercase;transition:background-color .1s linear;word-wrap:break-word;z-index:700}.admin__menu .level-0>a:focus{box-shadow:none}.admin__menu .level-0>a:before{content:'\e63a';display:block;font-size:2.2rem;height:2.2rem}.admin__menu .level-0>.submenu{background-color:#4a4542;box-shadow:0 0 3px #000;left:100%;min-height:calc(8.8rem + 2rem + 100%);padding:2rem 0 0;position:absolute;top:0;-ms-transform:translateX(-100%);transform:translateX(-100%);transition-duration:.3s;transition-property:transform,visibility;transition-timing-function:ease-in-out;visibility:hidden;z-index:697}.ie10 .admin__menu .level-0>.submenu,.ie11 .admin__menu .level-0>.submenu{height:100%}.admin__menu .level-0._show>.submenu{-ms-transform:translateX(0);transform:translateX(0);visibility:visible;z-index:698}.admin__menu .level-1{margin-left:1.5rem;margin-right:1.5rem}.admin__menu [class*=level-]:not(.level-0) a{display:block;padding:1.25rem 1.5rem}.admin__menu [class*=level-]:not(.level-0) a:hover{background-color:#403934}.admin__menu [class*=level-]:not(.level-0) a:active{background-color:#322c29;padding-bottom:1.15rem;padding-top:1.35rem}.admin__menu .submenu li{min-width:23.8rem}.admin__menu .submenu a{color:#fcfcfc;transition:background-color .1s linear}.admin__menu .submenu a:focus,.admin__menu .submenu a:hover{box-shadow:none;text-decoration:none}._keyfocus .admin__menu .submenu a:focus{background-color:#403934}._keyfocus .admin__menu .submenu a:active{background-color:#322c29}.admin__menu .submenu .parent{margin-bottom:4.5rem}.admin__menu .submenu .parent .submenu-group-title{color:#a79d95;display:block;font-size:1.6rem;font-weight:600;margin-bottom:.7rem;padding:1.25rem 1.5rem;pointer-events:none}.admin__menu .submenu .column{display:table-cell}.admin__menu .submenu-title{color:#fff;display:block;font-size:2.2rem;font-weight:600;margin-bottom:4.2rem;margin-left:3rem;margin-right:5.8rem}.admin__menu .submenu-sub-title{color:#fff;display:block;font-size:1.2rem;margin:-3.8rem 5.8rem 3.8rem 3rem}.admin__menu .action-close{padding:2.4rem 2.8rem;position:absolute;right:0;top:0}.admin__menu .action-close:before{color:#a79d95;font-size:1.7rem}.admin__menu .action-close:hover:before{color:#fff}.admin__menu .item-dashboard>a:before{content:'\e604';font-size:1.8rem;padding-top:.4rem}.admin__menu .item-sales>a:before{content:'\e60b'}.admin__menu .item-catalog>a:before{content:'\e608'}.admin__menu .item-customer>a:before{content:'\e603';font-size:2.6rem;position:relative;top:-.4rem}.admin__menu .item-marketing>a:before{content:'\e609';font-size:2rem;padding-top:.2rem}.admin__menu .item-content>a:before{content:'\e602';font-size:2.4rem;position:relative;top:-.2rem}.admin__menu .item-report>a:before{content:'\e60a'}.admin__menu .item-stores>a:before{content:'\e60d';font-size:1.9rem;padding-top:.3rem}.admin__menu .item-system>a:before{content:'\e610'}.admin__menu .item-partners._active>a:after,.admin__menu .item-system._current+.item-partners>a:after{display:none}.admin__menu .item-partners>a{padding-bottom:1rem}.admin__menu .item-partners>a:before{content:'\e612'}.admin__menu .level-0>.submenu>ul>.level-1:only-of-type>.submenu-group-title,.admin__menu .submenu .column:only-of-type .submenu-group-title{display:none}.admin__menu-overlay{bottom:0;left:0;position:fixed;right:0;top:0;z-index:697}.store-switcher{color:#333;float:left;font-size:1.3rem;margin-top:.7rem}.store-switcher .admin__action-dropdown{background-color:#f8f8f8;margin-left:.5em}.store-switcher .dropdown{display:inline-block;position:relative}.store-switcher .dropdown:after,.store-switcher .dropdown:before{content:'';display:table}.store-switcher .dropdown:after{clear:both}.store-switcher .dropdown .action.toggle{cursor:pointer;display:inline-block;text-decoration:none}.store-switcher .dropdown .action.toggle:after{-webkit-font-smoothing:antialiased;font-size:22px;line-height:2;color:#333;content:'\e607';font-family:icons-blank-theme;margin:0;vertical-align:top;display:inline-block;font-weight:400;overflow:hidden;speak:none;text-align:center}.store-switcher .dropdown .action.toggle:active:after,.store-switcher .dropdown .action.toggle:hover:after{color:#333}.store-switcher .dropdown .action.toggle.active{display:inline-block;text-decoration:none}.store-switcher .dropdown .action.toggle.active:after{-webkit-font-smoothing:antialiased;font-size:22px;line-height:2;color:#333;content:'\e618';font-family:icons-blank-theme;margin:0;vertical-align:top;display:inline-block;font-weight:400;overflow:hidden;speak:none;text-align:center}.store-switcher .dropdown .action.toggle.active:active:after,.store-switcher .dropdown .action.toggle.active:hover:after{color:#333}.store-switcher .dropdown .dropdown-menu{margin:4px 0 0;padding:0;list-style:none;background:#fff;border:1px solid #aaa6a0;min-width:19.5rem;z-index:100;box-sizing:border-box;display:none;position:absolute;top:100%;box-shadow:1px 1px 5px rgba(0,0,0,.5)}.store-switcher .dropdown .dropdown-menu li{margin:0;padding:0}.store-switcher .dropdown .dropdown-menu li:hover{background:0 0;cursor:pointer}.store-switcher .dropdown.active{overflow:visible}.store-switcher .dropdown.active .dropdown-menu{display:block}.store-switcher .dropdown-menu{left:0;margin-top:.5em;max-height:250px;overflow-y:auto;padding-top:.25em}.store-switcher .dropdown-menu li{border:0;cursor:default}.store-switcher .dropdown-menu li:hover{cursor:default}.store-switcher .dropdown-menu li a,.store-switcher .dropdown-menu li span{color:#333;display:block;padding:.5rem 1.3rem}.store-switcher .dropdown-menu li a{text-decoration:none}.store-switcher .dropdown-menu li a:hover{background:#e9e9e9}.store-switcher .dropdown-menu li span{color:#adadad;cursor:default}.store-switcher .dropdown-menu li.current span{background:#eee;color:#333}.store-switcher .dropdown-menu .store-switcher-store a,.store-switcher .dropdown-menu .store-switcher-store span{padding-left:2.6rem}.store-switcher .dropdown-menu .store-switcher-store-view a,.store-switcher .dropdown-menu .store-switcher-store-view span{padding-left:3.9rem}.store-switcher .dropdown-menu .dropdown-toolbar{border-top:1px solid #ebebeb;margin-top:1rem}.store-switcher .dropdown-menu .dropdown-toolbar a:before{content:'\e610';margin-right:.25em;position:relative;top:1px}.store-switcher-label{font-weight:700}.store-switcher-alt{display:inline-block;position:relative}.store-switcher-alt.active .dropdown-menu{display:block}.store-switcher-alt .dropdown-menu{margin-top:2px;white-space:nowrap}.store-switcher-alt .dropdown-menu ul{list-style:none;margin:0;padding:0}.store-switcher-alt strong{color:#a79d95;display:block;font-size:14px;font-weight:500;line-height:1.333;padding:5px 10px}.store-switcher-alt .store-selected{color:#676056;cursor:pointer;font-size:12px;font-weight:400;line-height:1.333}.store-switcher-alt .store-selected:after{-webkit-font-smoothing:antialiased;color:#afadac;content:'\e02c';font-style:normal;font-weight:400;margin:0 0 0 3px;speak:none;vertical-align:text-top}.store-switcher-alt .store-switcher-store,.store-switcher-alt .store-switcher-website{padding:0}.store-switcher-alt .store-switcher-store:hover,.store-switcher-alt .store-switcher-website:hover{background:0 0}.store-switcher-alt .manage-stores,.store-switcher-alt .store-switcher-all,.store-switcher-alt .store-switcher-store-view{padding:0}.store-switcher-alt .manage-stores>a,.store-switcher-alt .store-switcher-all>a{color:#676056;display:block;font-size:12px;padding:8px 15px;text-decoration:none}.store-switcher-website{margin:5px 0 0}.store-switcher-website>strong{padding-left:13px}.store-switcher-store{margin:1px 0 0}.store-switcher-store>strong{padding-left:20px}.store-switcher-store>ul{margin-top:1px}.store-switcher-store-view:first-child{border-top:1px solid #e5e5e5}.store-switcher-store-view>a{color:#333;display:block;font-size:13px;padding:5px 15px 5px 24px;text-decoration:none}.store-view:not(.store-switcher){float:left}.store-view .store-switcher-label{display:inline-block;margin-top:1rem}.tooltip{margin-left:.5em}.tooltip .help a,.tooltip .help span{cursor:pointer;display:inline-block;height:22px;position:relative;vertical-align:middle;width:22px;z-index:2}.tooltip .help a:before,.tooltip .help span:before{color:#333;content:'\e633';font-size:1.7rem}.tooltip .help a:hover{text-decoration:none}.tooltip .tooltip-content{background:#000;border-radius:3px;color:#fff;display:none;margin-left:-19px;margin-top:10px;max-width:200px;padding:4px 8px;position:absolute;text-shadow:none;z-index:20}.tooltip .tooltip-content:before{border-bottom:5px solid #000;border-left:5px solid transparent;border-right:5px solid transparent;content:'';height:0;left:20px;opacity:.8;position:absolute;top:-5px;width:0}.tooltip .tooltip-content.loading{position:absolute}.tooltip .tooltip-content.loading:before{border-bottom-color:rgba(0,0,0,.3)}.tooltip:hover>.tooltip-content{display:block}.page-actions._fixed,.page-main-actions:not(._hidden){background:#f8f8f8;border-bottom:1px solid #e3e3e3;border-top:1px solid #e3e3e3;padding:1.5rem}.page-main-actions{margin:0 0 3rem}.page-main-actions._hidden .store-switcher{display:none}.page-main-actions._hidden .page-actions-placeholder{min-height:50px}.page-actions{float:right}.page-main-actions .page-actions._fixed{left:8.8rem;position:fixed;right:0;top:0;z-index:501}.page-main-actions .page-actions._fixed .page-actions-inner:before{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#333;content:attr(data-title);float:left;font-size:2.8rem;margin-top:.3rem;max-width:50%}.page-actions .page-actions-buttons>button,.page-actions>button{float:right;margin-left:1.3rem}.page-actions .page-actions-buttons>button.action-back,.page-actions .page-actions-buttons>button.back,.page-actions>button.action-back,.page-actions>button.back{float:left;-ms-flex-order:-1;order:-1}.page-actions .page-actions-buttons>button.action-back:before,.page-actions .page-actions-buttons>button.back:before,.page-actions>button.action-back:before,.page-actions>button.back:before{content:'\e626';margin-right:.5em;position:relative;top:1px}.page-actions .page-actions-buttons>button.action-primary,.page-actions .page-actions-buttons>button.primary,.page-actions>button.action-primary,.page-actions>button.primary{-ms-flex-order:2;order:2}.page-actions .page-actions-buttons>button.save:not(.primary),.page-actions>button.save:not(.primary){-ms-flex-order:1;order:1}.page-actions .page-actions-buttons>button.delete,.page-actions>button.delete{-ms-flex-order:-1;order:-1}.page-actions .actions-split{float:right;margin-left:1.3rem;-ms-flex-order:2;order:2}.page-actions .actions-split .dropdown-menu .item{display:block}.page-actions-buttons{float:right;-ms-flex-pack:end;justify-content:flex-end;display:-ms-flexbox;display:flex}.customer-index-edit .page-actions-buttons{background-color:transparent}.admin__page-nav{background:#f1f1f1;border:1px solid #e3e3e3}.admin__page-nav._collapsed:first-child{border-bottom:none}.admin__page-nav._collapsed._show{border-bottom:1px solid #e3e3e3}.admin__page-nav._collapsed._show ._collapsible{background:#f1f1f1}.admin__page-nav._collapsed._show ._collapsible:after{content:'\e62b'}.admin__page-nav._collapsed._show ._collapsible+.admin__page-nav-items{display:block}.admin__page-nav._collapsed._hide .admin__page-nav-title-messages,.admin__page-nav._collapsed._hide .admin__page-nav-title-messages ._active{display:inline-block}.admin__page-nav+._collapsed{border-bottom:none;border-top:none}.admin__page-nav-title{border-bottom:1px solid #e3e3e3;color:#303030;display:block;font-size:1.4rem;line-height:1.2;margin:0 0 -1px;padding:1.8rem 1.5rem;position:relative;text-transform:uppercase}.admin__page-nav-title._collapsible{background:#fff;cursor:pointer;margin:0;padding-right:3.5rem;transition:border-color .1s ease-out,background-color .1s ease-out}.admin__page-nav-title._collapsible+.admin__page-nav-items{display:none;margin-top:-1px}.admin__page-nav-title._collapsible:after{content:'\e628';font-size:1.3rem;font-weight:700;position:absolute;right:1.8rem;top:2rem}.admin__page-nav-title._collapsible:hover{background:#f1f1f1}.admin__page-nav-title._collapsible:last-child{margin:0 0 -1px}.admin__page-nav-title strong{font-weight:700}.admin__page-nav-title .admin__page-nav-title-messages{display:none}.admin__page-nav-items{list-style-type:none;margin:0;padding:1rem 0 1.3rem}.admin__page-nav-item{border-left:3px solid transparent;margin-left:.7rem;padding:0;position:relative;transition:border-color .1s ease-out,background-color .1s ease-out}.admin__page-nav-item:hover{border-color:#e4e4e4}.admin__page-nav-item:hover .admin__page-nav-link{background:#e4e4e4;color:#303030;text-decoration:none}.admin__page-nav-item._active,.admin__page-nav-item.ui-state-active{border-color:#eb5202}.admin__page-nav-item._active .admin__page-nav-link,.admin__page-nav-item.ui-state-active .admin__page-nav-link{background:#fff;border-color:#e3e3e3;border-right:1px solid #fff;color:#303030;margin-right:-1px;font-weight:600}.admin__page-nav-item._loading:before,.admin__page-nav-item.ui-tabs-loading:before{display:none}.admin__page-nav-item._loading .admin__page-nav-item-message-loader,.admin__page-nav-item.ui-tabs-loading .admin__page-nav-item-message-loader{display:inline-block}.admin__page-nav-link{border:1px solid transparent;border-width:1px 0;color:#303030;display:block;font-weight:500;line-height:1.2;margin:0 0 -1px;padding:2rem 4rem 2rem 1rem;transition:border-color .1s ease-out,background-color .1s ease-out;word-wrap:break-word}.admin__page-nav-item-messages{display:inline-block}.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip{background:#f1f1f1;border:1px solid #f1f1f1;border-radius:1px;bottom:3.7rem;box-shadow:0 3px 9px 0 rgba(0,0,0,.3);display:none;font-size:1.4rem;font-weight:400;left:-1rem;line-height:1.36;padding:1.5rem;position:absolute;text-transform:none;width:27rem;word-break:normal;z-index:2}.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip:after,.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip:before{border:15px solid transparent;height:0;width:0;border-top-color:#f1f1f1;content:'';display:block;left:2rem;position:absolute;top:100%;z-index:3}.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip:after{border-top-color:#f1f1f1;margin-top:-1px;z-index:4}.admin__page-nav-item-messages .admin__page-nav-item-message-tooltip:before{border-top-color:#bfbfbf;margin-top:1px}.admin__page-nav-item-message-loader{display:none;margin-top:-1rem;position:absolute;right:0;top:50%}.admin__page-nav-item-message-loader .spinner{font-size:2rem;margin-right:1.5rem}._loading>.admin__page-nav-item-messages .admin__page-nav-item-message-loader{display:inline-block}.admin__page-nav-item-message{position:relative}.admin__page-nav-item-message:hover{z-index:500}.admin__page-nav-item-message:hover .admin__page-nav-item-message-tooltip{display:block}.admin__page-nav-item-message._changed,.admin__page-nav-item-message._error{display:none}.admin__page-nav-item-message .admin__page-nav-item-message-icon{display:inline-block;font-size:1.4rem;padding-left:.8em;vertical-align:baseline}.admin__page-nav-item-message .admin__page-nav-item-message-icon:after{color:#666;content:'\e631'}._changed:not(._error)>.admin__page-nav-item-messages ._changed{display:inline-block}._error .admin__page-nav-item-message-icon:after{color:#eb5202;content:'\e623'}._error>.admin__page-nav-item-messages ._error{display:inline-block}._error>.admin__page-nav-item-messages ._error .spinner{font-size:2rem;margin-right:1.5rem}._error .admin__page-nav-item-message-tooltip{background:#f1f1f1;border:1px solid #f1f1f1;border-radius:1px;bottom:3.7rem;box-shadow:0 3px 9px 0 rgba(0,0,0,.3);display:none;font-weight:400;left:-1rem;line-height:1.36;padding:2rem;position:absolute;text-transform:none;width:27rem;word-break:normal;z-index:2}._error .admin__page-nav-item-message-tooltip:after,._error .admin__page-nav-item-message-tooltip:before{border:15px solid transparent;height:0;width:0;border-top-color:#f1f1f1;content:'';display:block;left:2rem;position:absolute;top:100%;z-index:3}._error .admin__page-nav-item-message-tooltip:after{border-top-color:#f1f1f1;margin-top:-1px;z-index:4}._error .admin__page-nav-item-message-tooltip:before{border-top-color:#bfbfbf}.admin__data-grid-wrap-static .data-grid{box-sizing:border-box}.admin__data-grid-wrap-static .data-grid thead{color:#333}.admin__data-grid-wrap-static .data-grid tr:nth-child(even) td{background-color:#f5f5f5}.admin__data-grid-wrap-static .data-grid tr:nth-child(even) td._dragging{background-color:rgba(245,245,245,.95)}.admin__data-grid-wrap-static .data-grid ul{margin-left:1rem;padding-left:1rem}.admin__data-grid-wrap-static .admin__data-grid-loading-mask{background:rgba(255,255,255,.5);bottom:0;left:0;position:absolute;right:0;top:0;z-index:399}.admin__data-grid-wrap-static .admin__data-grid-loading-mask .grid-loader{background:url(../images/loader-2.gif) 50% 50% no-repeat;bottom:0;height:149px;left:0;margin:auto;position:absolute;right:0;top:0;width:218px}.data-grid-filters-actions-wrap{float:right}.data-grid-search-control-wrap{float:left;max-width:45.5rem;position:relative;width:35%}.data-grid-search-control-wrap :-ms-input-placeholder{font-style:italic}.data-grid-search-control-wrap ::-webkit-input-placeholder{font-style:italic}.data-grid-search-control-wrap ::-moz-placeholder{font-style:italic}.data-grid-search-control-wrap .action-submit{background-color:transparent;border:none;border-radius:0;box-shadow:none;margin:0;padding:.6rem 2rem .2rem;position:absolute;right:0;top:1px}.data-grid-search-control-wrap .action-submit:hover{background-color:transparent;border:none;box-shadow:none}.data-grid-search-control-wrap .action-submit:active{-ms-transform:scale(0.9);transform:scale(0.9)}.data-grid-search-control-wrap .action-submit:hover:before{color:#1a1a1a}._keyfocus .data-grid-search-control-wrap .action-submit:focus{box-shadow:0 0 0 1px #008bdb}.data-grid-search-control-wrap .action-submit:before{content:'\e60c';font-size:2rem;transition:color .1s linear}.data-grid-search-control-wrap .action-submit>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.data-grid-search-control-wrap .abs-action-menu .action-submenu,.data-grid-search-control-wrap .abs-action-menu .action-submenu .action-submenu,.data-grid-search-control-wrap .action-menu,.data-grid-search-control-wrap .action-menu .action-submenu,.data-grid-search-control-wrap .actions-split .action-menu .action-submenu,.data-grid-search-control-wrap .actions-split .action-menu .action-submenu .action-submenu,.data-grid-search-control-wrap .actions-split .dropdown-menu .action-submenu,.data-grid-search-control-wrap .actions-split .dropdown-menu .action-submenu .action-submenu{max-height:19.25rem;overflow-y:auto;z-index:398}.data-grid-search-control-wrap .action-menu-item._selected{background-color:#e0f6fe}.data-grid-search-control-wrap .data-grid-search-label{display:none}.data-grid-search-control{padding-right:6rem;width:100%}.data-grid-filters-action-wrap{float:left;padding-left:2rem}.data-grid-filters-action-wrap .action-default{font-size:1.3rem;margin-bottom:1rem;padding-left:1.7rem;padding-right:2.1rem;padding-top:.7rem}.data-grid-filters-action-wrap .action-default._active{background-color:#fff;border-bottom-color:#fff;border-right-color:#ccc;font-weight:600;margin:-.1rem 0 0;padding-bottom:1.6rem;padding-top:.8rem;position:relative;z-index:281}.data-grid-filters-action-wrap .action-default._active:after{background-color:#eb5202;bottom:100%;content:'';height:3px;left:-1px;position:absolute;right:-1px}.data-grid-filters-action-wrap .action-default:before{color:#333;content:'\e605';font-size:1.8rem;margin-right:.4rem;position:relative;top:-1px;vertical-align:top}.data-grid-filters-action-wrap .filters-active{display:none}.admin__action-grid-select .admin__control-select{margin:-.5rem .5rem 0 0;padding-bottom:.6rem;padding-top:.6rem}.admin__data-grid-filters-wrap{opacity:0;visibility:hidden;clear:both;font-size:1.3rem;transition:opacity .3s ease}.admin__data-grid-filters-wrap._show{opacity:1;visibility:visible;border-bottom:1px solid #ccc;border-top:1px solid #ccc;margin-bottom:.7rem;padding:3.6rem 0 3rem;position:relative;top:-1px;z-index:280}.admin__data-grid-filters-wrap._show .admin__data-grid-filters,.admin__data-grid-filters-wrap._show .admin__data-grid-filters-footer{display:block}.admin__data-grid-filters-wrap .admin__form-field-label,.admin__data-grid-filters-wrap .admin__form-field-legend{display:block;font-weight:700;margin:0 0 .3rem;text-align:left}.admin__data-grid-filters-wrap .admin__form-field{display:inline-block;margin-bottom:2em;margin-left:0;padding-left:2rem;padding-right:2rem;vertical-align:top;width:calc(100% / 4 - 4px)}.admin__data-grid-filters-wrap .admin__form-field .admin__form-field{display:block;float:none;margin-bottom:1.5rem;padding-left:0;padding-right:0;width:auto}.admin__data-grid-filters-wrap .admin__form-field .admin__form-field:last-child{margin-bottom:0}.admin__data-grid-filters-wrap .admin__form-field .admin__form-field .admin__form-field-label{border:1px solid transparent;float:left;font-weight:400;line-height:1.36;margin-bottom:0;padding-bottom:.6rem;padding-right:1em;padding-top:.6rem;width:25%}.admin__data-grid-filters-wrap .admin__form-field .admin__form-field .admin__form-field-control{margin-left:25%}.admin__data-grid-filters-wrap .admin__action-multiselect,.admin__data-grid-filters-wrap .admin__control-select,.admin__data-grid-filters-wrap .admin__control-text,.admin__data-grid-filters-wrap .admin__form-field-label{font-size:1.3rem}.admin__data-grid-filters-wrap .admin__control-select{height:3.2rem;padding-top:.5rem}.admin__data-grid-filters-wrap .admin__action-multiselect:before{height:3.2rem;width:3.2rem}.admin__data-grid-filters-wrap .admin__control-select,.admin__data-grid-filters-wrap .admin__control-text._has-datepicker{width:100%}.admin__data-grid-filters{display:none;margin-left:-2rem;margin-right:-2rem}.admin__filters-legend{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.admin__data-grid-filters-footer{display:none;font-size:1.4rem}.admin__data-grid-filters-footer .admin__footer-main-actions{margin-left:25%;text-align:right}.admin__data-grid-filters-footer .admin__footer-secondary-actions{float:left;width:50%}.admin__data-grid-filters-current{border-bottom:.1rem solid #ccc;border-top:.1rem solid #ccc;display:none;font-size:1.3rem;margin-bottom:.9rem;padding-bottom:.8rem;padding-top:1.1rem;width:100%}.admin__data-grid-filters-current._show{display:table;position:relative;top:-1px;z-index:3}.admin__data-grid-filters-current._show+.admin__data-grid-filters-wrap._show{margin-top:-1rem}.admin__current-filters-actions-wrap,.admin__current-filters-list-wrap,.admin__current-filters-title-wrap{display:table-cell;vertical-align:top}.admin__current-filters-title{margin-right:1em;white-space:nowrap}.admin__current-filters-list-wrap{width:100%}.admin__current-filters-list{margin-bottom:0}.admin__current-filters-list>li{display:inline-block;font-weight:600;margin:0 1rem .5rem;padding-right:2.6rem;position:relative}.admin__current-filters-list .action-remove{background-color:transparent;border:none;border-radius:0;box-shadow:none;margin:0;padding:0;line-height:1;position:absolute;right:0;top:1px}.admin__current-filters-list .action-remove:hover{background-color:transparent;border:none;box-shadow:none}.admin__current-filters-list .action-remove:hover:before{color:#949494}.admin__current-filters-list .action-remove:active{-ms-transform:scale(0.9);transform:scale(0.9)}.admin__current-filters-list .action-remove:before{color:#adadad;content:'\e620';font-size:1.6rem;transition:color .1s linear}.admin__current-filters-list .action-remove>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.admin__current-filters-actions-wrap .action-clear{border:none;padding-bottom:0;padding-top:0;white-space:nowrap}.admin__data-grid-pager-wrap{float:right;text-align:right}.admin__data-grid-pager{display:inline-block;margin-left:3rem}.admin__data-grid-pager .admin__control-text::-webkit-inner-spin-button,.admin__data-grid-pager .admin__control-text::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.admin__data-grid-pager .admin__control-text{-moz-appearance:textfield;text-align:center;width:4.4rem}.action-next,.action-previous{width:4.4rem}.action-next:before,.action-previous:before{font-weight:700}.action-next>span,.action-previous>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.action-previous{margin-right:2.5rem;text-indent:-.25em}.action-previous:before{content:'\e629'}.action-next{margin-left:1.5rem;text-indent:.1em}.action-next:before{content:'\e62a'}.admin__data-grid-action-bookmarks{opacity:.98}.admin__data-grid-action-bookmarks .admin__action-dropdown-text:after{left:0;right:-6px}.admin__data-grid-action-bookmarks._active{z-index:290}.admin__data-grid-action-bookmarks .admin__action-dropdown .admin__action-dropdown-text{display:inline-block;max-width:15rem;min-width:4.9rem;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.admin__data-grid-action-bookmarks .admin__action-dropdown:before{content:'\e60f'}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu{font-size:1.3rem;left:0;padding:1rem 0;right:auto}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu>li{padding:0 5rem 0 0;position:relative;white-space:nowrap}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu>li:not(.action-dropdown-menu-action){transition:background-color .1s linear}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu>li:not(.action-dropdown-menu-action):hover{background-color:#e3e3e3}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item{max-width:23rem;min-width:18rem;white-space:normal;word-break:break-all}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item-edit{display:none;padding-bottom:1rem;padding-left:1rem;padding-top:1rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item-edit .action-dropdown-menu-item-actions{padding-bottom:1rem;padding-top:1rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-action{padding-left:1rem;padding-top:1rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-action+.action-dropdown-menu-item-last{padding-top:.5rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-action>a{color:#008bdb;text-decoration:none;display:inline-block;padding-left:1.1rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-action>a:hover{color:#0fa7ff;text-decoration:underline}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item-last{padding-bottom:0}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu ._edit .action-dropdown-menu-item{display:none}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu ._edit .action-dropdown-menu-item-edit{display:block}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu ._active .action-dropdown-menu-link{font-weight:600}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .admin__control-text{font-size:1.3rem;min-width:15rem;width:calc(100% - 4rem)}.ie9 .admin__data-grid-action-bookmarks .admin__action-dropdown-menu .admin__control-text{width:15rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-item-actions{border-left:1px solid #fff;bottom:0;position:absolute;right:0;top:0;width:5rem}.admin__data-grid-action-bookmarks .admin__action-dropdown-menu .action-dropdown-menu-link{color:#333;display:block;text-decoration:none;padding:1rem 1rem 1rem 2.1rem}.admin__data-grid-action-bookmarks .action-delete,.admin__data-grid-action-bookmarks .action-edit,.admin__data-grid-action-bookmarks .action-submit{background-color:transparent;border:none;border-radius:0;box-shadow:none;margin:0;vertical-align:top}.admin__data-grid-action-bookmarks .action-delete:hover,.admin__data-grid-action-bookmarks .action-edit:hover,.admin__data-grid-action-bookmarks .action-submit:hover{background-color:transparent;border:none;box-shadow:none}.admin__data-grid-action-bookmarks .action-delete:before,.admin__data-grid-action-bookmarks .action-edit:before,.admin__data-grid-action-bookmarks .action-submit:before{font-size:1.7rem}.admin__data-grid-action-bookmarks .action-delete>span,.admin__data-grid-action-bookmarks .action-edit>span,.admin__data-grid-action-bookmarks .action-submit>span{clip:rect(0,0,0,0);overflow:hidden;position:absolute}.admin__data-grid-action-bookmarks .action-delete,.admin__data-grid-action-bookmarks .action-edit{padding:.6rem 1.4rem}.admin__data-grid-action-bookmarks .action-delete:active,.admin__data-grid-action-bookmarks .action-edit:active{-ms-transform:scale(0.9);transform:scale(0.9)}.admin__data-grid-action-bookmarks .action-submit{padding:.6rem 1rem .6rem .8rem}.admin__data-grid-action-bookmarks .action-submit:active{position:relative;right:-1px}.admin__data-grid-action-bookmarks .action-submit:before{content:'\e625'}.admin__data-grid-action-bookmarks .action-delete:before{content:'\e630'}.admin__data-grid-action-bookmarks .action-edit{padding-top:.8rem}.admin__data-grid-action-bookmarks .action-edit:before{content:'\e631'}.admin__data-grid-action-columns._active{opacity:.98;z-index:290}.admin__data-grid-action-columns .admin__action-dropdown:before{content:'\e610';font-size:1.8rem;margin-right:.7rem;vertical-align:top}.admin__data-grid-action-columns-menu{color:#303030;font-size:1.3rem;overflow:hidden;padding:2.2rem 3.5rem 1rem;z-index:1}.admin__data-grid-action-columns-menu._overflow .admin__action-dropdown-menu-header{border-bottom:1px solid #d1d1d1}.admin__data-grid-action-columns-menu._overflow .admin__action-dropdown-menu-content{width:49.2rem}.admin__data-grid-action-columns-menu._overflow .admin__action-dropdown-menu-footer{border-top:1px solid #d1d1d1;padding-top:2.5rem}.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-content{max-height:22.85rem;overflow-y:auto;padding-top:1.5rem;position:relative;width:47.4rem}.admin__data-grid-action-columns-menu .admin__field-option{float:left;height:1.9rem;margin-bottom:1.5rem;padding:0 1rem 0 0;width:15.8rem}.admin__data-grid-action-columns-menu .admin__field-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-header{padding-bottom:1.5rem}.admin__data-grid-action-columns-menu .admin__action-dropdown-menu-footer{padding:1rem 0 2rem}.admin__data-grid-action-columns-menu .admin__action-dropdown-footer-main-actions{margin-left:25%;text-align:right}.admin__data-grid-action-columns-menu .admin__action-dropdown-footer-secondary-actions{float:left;margin-left:-1em}.admin__data-grid-action-export._active{opacity:.98;z-index:290}.admin__data-grid-action-export .admin__action-dropdown:before{content:'\e635';font-size:1.7rem;left:.3rem;margin-right:.7rem;vertical-align:top}.admin__data-grid-action-export-menu{padding-left:2rem;padding-right:2rem;padding-top:1rem}.admin__data-grid-action-export-menu .admin__action-dropdown-footer-main-actions{padding-bottom:2rem;padding-top:2.5rem;white-space:nowrap}.sticky-header{background-color:#f8f8f8;border-bottom:1px solid #e3e3e3;box-shadow:0 5px 5px 0 rgba(0,0,0,.25);left:8.8rem;margin-top:-1px;padding:.5rem 3rem 0;position:fixed;right:0;top:77px;z-index:398}.sticky-header .admin__data-grid-wrap{margin-bottom:0;overflow-x:visible;padding-bottom:0}.sticky-header .admin__data-grid-header-row{position:relative;text-align:right}.sticky-header .admin__data-grid-header-row:last-child{margin:0}.sticky-header .admin__data-grid-actions-wrap,.sticky-header .admin__data-grid-filters-wrap,.sticky-header .admin__data-grid-pager-wrap,.sticky-header .data-grid-filters-actions-wrap,.sticky-header .data-grid-search-control-wrap{display:inline-block;float:none;vertical-align:top}.sticky-header .action-select-wrap{float:left;margin-right:1.5rem;width:16.66666667%}.sticky-header .admin__control-support-text{float:left}.sticky-header .data-grid-search-control-wrap{margin:-.5rem 0 0 1.1rem;width:auto}.sticky-header .data-grid-search-control-wrap .data-grid-search-label{box-sizing:border-box;cursor:pointer;display:block;min-width:3.8rem;padding:1.2rem .6rem 1.7rem;position:relative;text-align:center}.sticky-header .data-grid-search-control-wrap .data-grid-search-label:before{color:#333;content:'\e60c';font-size:2rem;transition:color .1s linear}.sticky-header .data-grid-search-control-wrap .data-grid-search-label:hover:before{color:#000}.sticky-header .data-grid-search-control-wrap .data-grid-search-label span{display:none}.sticky-header .data-grid-filters-actions-wrap{margin:-.5rem 0 0 1.1rem;padding-left:0;position:relative}.sticky-header .data-grid-filters-actions-wrap .action-default{background-color:transparent;border:1px solid transparent;box-sizing:border-box;min-width:3.8rem;padding:1.2rem .6rem 1.7rem;text-align:center;transition:all .15s ease}.sticky-header .data-grid-filters-actions-wrap .action-default span{display:none}.sticky-header .data-grid-filters-actions-wrap .action-default:before{margin:0}.sticky-header .data-grid-filters-actions-wrap .action-default._active{background-color:#fff;border-color:#adadad #adadad #fff;box-shadow:1px 1px 5px rgba(0,0,0,.5);z-index:210}.sticky-header .data-grid-filters-actions-wrap .action-default._active:after{background-color:#fff;content:'';height:6px;left:-2px;position:absolute;right:-6px;top:100%}.sticky-header .data-grid-filters-action-wrap{padding:0}.sticky-header .admin__data-grid-filters-wrap{background-color:#fff;border:1px solid #adadad;box-shadow:0 5px 5px 0 rgba(0,0,0,.25);left:0;padding-left:3.5rem;padding-right:3.5rem;position:absolute;top:100%;width:100%;z-index:209}.sticky-header .admin__data-grid-filters-current+.admin__data-grid-filters-wrap._show{margin-top:-6px}.sticky-header .filters-active{background-color:#e04f00;border-radius:10px;color:#fff;display:block;font-size:1.4rem;font-weight:700;padding:.1rem .7rem;position:absolute;right:-7px;top:0;z-index:211}.sticky-header .filters-active:empty{padding-bottom:0;padding-top:0}.sticky-header .admin__data-grid-actions-wrap{margin:-.5rem 0 0 1.1rem;padding-right:.3rem}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown{background-color:transparent;box-sizing:border-box;min-width:3.8rem;padding-left:.6rem;padding-right:.6rem;text-align:center}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown .admin__action-dropdown-text{display:inline-block;max-width:0;min-width:0;overflow:hidden}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown:before{margin:0}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown-wrap{margin-right:1.1rem}.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown-wrap:after,.sticky-header .admin__data-grid-actions-wrap .admin__action-dropdown:after{display:none}.sticky-header .admin__data-grid-actions-wrap ._active .admin__action-dropdown{background-color:#fff}.sticky-header .admin__data-grid-action-bookmarks .admin__action-dropdown:before{position:relative;top:-3px}.sticky-header .admin__data-grid-filters-current{border-bottom:0;border-top:0;margin-bottom:0;padding-bottom:0;padding-top:0}.sticky-header .admin__data-grid-pager .admin__control-text,.sticky-header .admin__data-grid-pager-wrap .admin__control-support-text,.sticky-header .data-grid-search-control-wrap .action-submit,.sticky-header .data-grid-search-control-wrap .data-grid-search-control{display:none}.sticky-header .action-next{margin:0}.sticky-header .data-grid{margin-bottom:-1px}.data-grid-cap-left,.data-grid-cap-right{background-color:#f8f8f8;bottom:-2px;position:absolute;top:6rem;width:3rem;z-index:201}.data-grid-cap-left{left:0}.admin__data-grid-header{font-size:1.4rem}.admin__data-grid-header-row+.admin__data-grid-header-row{margin-top:1.1rem}.admin__data-grid-header-row:last-child{margin-bottom:0}.admin__data-grid-header-row .action-select-wrap{display:block}.admin__data-grid-header-row .action-select{width:100%}.admin__data-grid-actions-wrap{float:right;margin-left:1.1rem;margin-top:-.5rem;text-align:right}.admin__data-grid-actions-wrap .admin__action-dropdown-wrap{position:relative;text-align:left;vertical-align:middle}.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._active+.admin__action-dropdown-wrap:after,.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._active:after,.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._hide+.admin__action-dropdown-wrap:after,.admin__data-grid-actions-wrap .admin__action-dropdown-wrap:first-child:after{display:none}.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._active .admin__action-dropdown,.admin__data-grid-actions-wrap .admin__action-dropdown-wrap._active .admin__action-dropdown-menu{border-color:#adadad}.admin__data-grid-actions-wrap .admin__action-dropdown-wrap:after{border-left:1px solid #ccc;content:'';height:3.2rem;left:0;position:absolute;top:.5rem;z-index:3}.admin__data-grid-actions-wrap .admin__action-dropdown{padding-bottom:1.7rem;padding-top:1.2rem}.admin__data-grid-actions-wrap .admin__action-dropdown:after{margin-top:-.4rem}.admin__data-grid-outer-wrap{min-height:8rem;position:relative}.admin__data-grid-wrap{margin-bottom:2rem;max-width:100%;overflow-x:auto;padding-bottom:1rem;padding-top:2rem}.admin__data-grid-loading-mask{background:rgba(255,255,255,.5);bottom:0;left:0;position:absolute;right:0;top:0;z-index:399}.admin__data-grid-loading-mask .spinner{font-size:4rem;left:50%;margin-left:-2rem;margin-top:-2rem;position:absolute;top:50%}.ie9 .admin__data-grid-loading-mask .spinner{background:url(../images/loader-2.gif) 50% 50% no-repeat;bottom:0;height:149px;left:0;margin:auto;position:absolute;right:0;top:0;width:218px}.data-grid-cell-content{display:inline-block;overflow:hidden;width:100%}body._in-resize{cursor:col-resize;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body._in-resize *,body._in-resize .data-grid-th,body._in-resize .data-grid-th._draggable,body._in-resize .data-grid-th._sortable{cursor:col-resize!important}._layout-fixed{table-layout:fixed}.data-grid{border:none;font-size:1.3rem;margin-bottom:0;width:100%}.data-grid:not(._dragging-copy) ._odd-row td._dragging{background-color:#d0d0d0}.data-grid:not(._dragging-copy) ._dragging{background-color:#d9d9d9;color:rgba(48,48,48,.95)}.data-grid:not(._dragging-copy) ._dragging a{color:rgba(0,139,219,.95)}.data-grid:not(._dragging-copy) ._dragging a:hover{color:rgba(15,167,255,.95)}.data-grid._dragged{outline:#007bdb solid 1px}.data-grid thead{background-color:transparent}.data-grid tfoot th{padding:1rem}.data-grid tr._odd-row td{background-color:#f5f5f5}.data-grid tr._odd-row td._update-status-active{background:#89e1ff}.data-grid tr._odd-row td._update-status-upcoming{background:#b7ee63}.data-grid tr:hover td._update-status-active,.data-grid tr:hover td._update-status-upcoming{background-color:#e5f7fe}.data-grid tr.data-grid-tr-no-data td{font-size:1.6rem;padding:3rem;text-align:center}.data-grid tr.data-grid-tr-no-data:hover td{background-color:#fff;cursor:default}.data-grid tr:active td{background-color:#e0f6fe}.data-grid tr:hover td{background-color:#e5f7fe}.data-grid tr._dragged td{background:#d0d0d0}.data-grid tr._dragover-top td{box-shadow:inset 0 3px 0 0 #008bdb}.data-grid tr._dragover-bottom td{box-shadow:inset 0 -3px 0 0 #008bdb}.data-grid tr:not(.data-grid-editable-row):last-child td{border-bottom:.1rem solid #d6d6d6}.data-grid tr ._clickable,.data-grid tr._clickable{cursor:pointer}.data-grid tr._disabled{pointer-events:none}.data-grid td,.data-grid th{font-size:1.3rem;line-height:1.36;transition:background-color .1s linear;vertical-align:top}.data-grid td._resizing,.data-grid th._resizing{border-left:1px solid #007bdb;border-right:1px solid #007bdb}.data-grid td._hidden,.data-grid th._hidden{display:none}.data-grid td._fit,.data-grid th._fit{width:1%}.data-grid td{background-color:#fff;border-left:.1rem dashed #d6d6d6;border-right:.1rem dashed #d6d6d6;color:#303030;padding:1rem}.data-grid td:first-child{border-left-style:solid}.data-grid td:last-child{border-right-style:solid}.data-grid td .action-select-wrap{position:static}.data-grid td .action-select{color:#008bdb;text-decoration:none;background-color:transparent;border:none;font-size:1.3rem;padding:0 3rem 0 0;position:relative}.data-grid td .action-select:hover{color:#0fa7ff;text-decoration:underline}.data-grid td .action-select:hover:after{border-color:#0fa7ff transparent transparent}.data-grid td .action-select:after{border-color:#008bdb transparent transparent;margin:.6rem 0 0 .7rem;right:auto;top:auto}.data-grid td .action-select:before{display:none}.data-grid td .abs-action-menu .action-submenu,.data-grid td .abs-action-menu .action-submenu .action-submenu,.data-grid td .action-menu,.data-grid td .action-menu .action-submenu,.data-grid td .actions-split .action-menu .action-submenu,.data-grid td .actions-split .action-menu .action-submenu .action-submenu,.data-grid td .actions-split .dropdown-menu .action-submenu,.data-grid td .actions-split .dropdown-menu .action-submenu .action-submenu{left:auto;min-width:10rem;right:0;text-align:left;top:auto;z-index:1}.data-grid td._update-status-active{background:#bceeff}.data-grid td._update-status-upcoming{background:#ccf391}.data-grid th{background-color:#514943;border:.1rem solid #8a837f;border-left-color:transparent;color:#fff;font-weight:600;padding:0;text-align:left}.data-grid th:first-child{border-left-color:#8a837f}.data-grid th._dragover-left{box-shadow:inset 3px 0 0 0 #fff;z-index:2}.data-grid th._dragover-right{box-shadow:inset -3px 0 0 0 #fff}.data-grid .shadow-div{cursor:col-resize;height:100%;margin-right:-5px;position:absolute;right:0;top:0;width:10px}.data-grid .data-grid-th{background-clip:padding-box;color:#fff;padding:1rem;position:relative;vertical-align:middle}.data-grid .data-grid-th._resize-visible .shadow-div{cursor:auto;display:none}.data-grid .data-grid-th._draggable{cursor:grab}.data-grid .data-grid-th._sortable{cursor:pointer;transition:background-color .1s linear;z-index:1}.data-grid .data-grid-th._sortable:focus,.data-grid .data-grid-th._sortable:hover{background-color:#5f564f}.data-grid .data-grid-th._sortable:active{padding-bottom:.9rem;padding-top:1.1rem}.data-grid .data-grid-th.required>span:after{color:#f38a5e;content:'*';margin-left:.3rem}.data-grid .data-grid-checkbox-cell{overflow:hidden;padding:0;vertical-align:top;width:5.2rem}.data-grid .data-grid-checkbox-cell:hover{cursor:default}.data-grid .data-grid-thumbnail-cell{text-align:center;width:7rem}.data-grid .data-grid-thumbnail-cell img{border:1px solid #d6d6d6;width:5rem}.data-grid .data-grid-multicheck-cell{padding:1rem 1rem .9rem;text-align:center;vertical-align:middle}.data-grid .data-grid-onoff-cell{text-align:center;width:12rem}.data-grid .data-grid-actions-cell{padding-left:2rem;padding-right:2rem;text-align:center;width:1%}.data-grid._hidden{display:none}.data-grid._dragging-copy{box-shadow:1px 1px 5px rgba(0,0,0,.5);left:0;opacity:.95;position:fixed;top:0;z-index:1000}.data-grid._dragging-copy .data-grid-th{border:1px solid #007bdb;border-bottom:none}.data-grid._dragging-copy .data-grid-th,.data-grid._dragging-copy .data-grid-th._sortable{cursor:grabbing}.data-grid._dragging-copy tr:last-child td{border-bottom:1px solid #007bdb}.data-grid._dragging-copy td{border-left:1px solid #007bdb;border-right:1px solid #007bdb}.data-grid._dragging-copy._in-edit .data-grid-editable-row.data-grid-bulk-edit-panel td,.data-grid._dragging-copy._in-edit .data-grid-editable-row.data-grid-bulk-edit-panel td:before,.data-grid._dragging-copy._in-edit .data-grid-editable-row.data-grid-bulk-edit-panel:hover td{background-color:rgba(255,251,230,.95)}.data-grid._dragging-copy._in-edit .data-grid-editable-row td,.data-grid._dragging-copy._in-edit .data-grid-editable-row:hover td{background-color:rgba(255,255,255,.95)}.data-grid._dragging-copy._in-edit .data-grid-editable-row td:after,.data-grid._dragging-copy._in-edit .data-grid-editable-row td:before{left:0;right:0}.data-grid._dragging-copy._in-edit .data-grid-editable-row td:before{background-color:rgba(255,255,255,.95)}.data-grid._dragging-copy._in-edit .data-grid-editable-row td:only-child{border-left:1px solid #007bdb;border-right:1px solid #007bdb;left:0}.data-grid._dragging-copy._in-edit .data-grid-editable-row .admin__control-select,.data-grid._dragging-copy._in-edit .data-grid-editable-row .admin__control-text{opacity:.5}.data-grid .data-grid-controls-row td{padding-top:1.6rem}.data-grid .data-grid-controls-row td.data-grid-checkbox-cell{padding-top:.6rem}.data-grid .data-grid-controls-row td [class*=admin__control-],.data-grid .data-grid-controls-row td button{margin-top:-1.7rem}.data-grid._in-edit tr:hover td{background-color:#e6e6e6}.data-grid._in-edit ._odd-row.data-grid-editable-row td,.data-grid._in-edit ._odd-row.data-grid-editable-row:hover td{background-color:#fff}.data-grid._in-edit ._odd-row td,.data-grid._in-edit ._odd-row:hover td{background-color:#dcdcdc}.data-grid._in-edit .data-grid-editable-row-actions td,.data-grid._in-edit .data-grid-editable-row-actions:hover td{background-color:#fff}.data-grid._in-edit td{background-color:#e6e6e6;pointer-events:none}.data-grid._in-edit .data-grid-checkbox-cell{pointer-events:auto}.data-grid._in-edit .data-grid-editable-row{border:.1rem solid #adadad;border-bottom-color:#c2c2c2}.data-grid._in-edit .data-grid-editable-row:hover td{background-color:#fff}.data-grid._in-edit .data-grid-editable-row td{background-color:#fff;border-bottom-color:#fff;border-left-style:hidden;border-right-style:hidden;border-top-color:#fff;pointer-events:auto;vertical-align:middle}.data-grid._in-edit .data-grid-editable-row td:first-child{border-left-color:#adadad;border-left-style:solid}.data-grid._in-edit .data-grid-editable-row td:first-child:after,.data-grid._in-edit .data-grid-editable-row td:first-child:before{left:0}.data-grid._in-edit .data-grid-editable-row td:last-child{border-right-color:#adadad;border-right-style:solid;left:-.1rem}.data-grid._in-edit .data-grid-editable-row td:last-child:after,.data-grid._in-edit .data-grid-editable-row td:last-child:before{right:0}.data-grid._in-edit .data-grid-editable-row .admin__control-select,.data-grid._in-edit .data-grid-editable-row .admin__control-text{width:100%}.data-grid._in-edit .data-grid-bulk-edit-panel td{vertical-align:bottom}.data-grid .data-grid-editable-row td{border-left-color:#fff;border-left-style:solid;position:relative;z-index:1}.data-grid .data-grid-editable-row td:after{bottom:0;box-shadow:0 5px 5px rgba(0,0,0,.25);content:'';height:.9rem;left:0;margin-top:-1rem;position:absolute;right:0}.data-grid .data-grid-editable-row td:before{background-color:#fff;bottom:0;content:'';height:1rem;left:-10px;position:absolute;right:-10px;z-index:1}.data-grid .data-grid-editable-row.data-grid-editable-row-actions td,.data-grid .data-grid-editable-row.data-grid-editable-row-actions:hover td{background-color:#fff}.data-grid .data-grid-editable-row.data-grid-editable-row-actions td:first-child{border-left-color:#fff;border-right-color:#fff}.data-grid .data-grid-editable-row.data-grid-editable-row-actions td:last-child{left:0}.data-grid .data-grid-editable-row.data-grid-bulk-edit-panel td,.data-grid .data-grid-editable-row.data-grid-bulk-edit-panel td:before,.data-grid .data-grid-editable-row.data-grid-bulk-edit-panel:hover td{background-color:#fffbe6}.data-grid .data-grid-editable-row-actions{left:50%;margin-left:-12.5rem;margin-top:-2px;position:absolute;text-align:center}.data-grid .data-grid-editable-row-actions td{width:25rem}.data-grid .data-grid-editable-row-actions [class*=action-]{min-width:9rem}.data-grid .data-grid-draggable-row-cell{width:1%}.data-grid .data-grid-draggable-row-cell .draggable-handle{padding:0}.data-grid-th._sortable._ascend,.data-grid-th._sortable._descend{padding-right:2.7rem}.data-grid-th._sortable._ascend:before,.data-grid-th._sortable._descend:before{margin-top:-1em;position:absolute;right:1rem;top:50%}.data-grid-th._sortable._ascend:before{content:'\2193'}.data-grid-th._sortable._descend:before{content:'\2191'}.data-grid-checkbox-cell-inner{display:block;padding:1.1rem 1.8rem .9rem;text-align:right}.data-grid-checkbox-cell-inner:hover{cursor:pointer}.data-grid-state-cell-inner{display:block;padding:1.1rem 1.8rem .9rem;text-align:center}.data-grid-state-cell-inner>span{display:inline-block;font-style:italic;padding:.6rem 0}.data-grid-row-parent._active>td .data-grid-checkbox-cell-inner:before{content:'\e62b'}.data-grid-row-parent>td .data-grid-checkbox-cell-inner{padding-left:3.7rem;position:relative}.data-grid-row-parent>td .data-grid-checkbox-cell-inner:before{content:'\e628';font-size:1rem;font-weight:700;left:1.35rem;position:absolute;top:1.6rem}.data-grid-th._col-xs{width:1%}.data-grid-info-panel{box-shadow:0 0 5px rgba(0,0,0,.5);margin:2rem .1rem -2rem}.data-grid-info-panel .messages{overflow:hidden}.data-grid-info-panel .messages .message{margin:1rem}.data-grid-info-panel .messages .message:last-child{margin-bottom:1rem}.data-grid-info-panel-actions{padding:1rem;text-align:right}.data-grid-editable-row .admin__field-control{position:relative}.data-grid-editable-row .admin__field-control._error:after{border-color:transparent #ee7d7d transparent transparent;border-style:solid;border-width:0 12px 12px 0;content:'';position:absolute;right:0;top:0}.data-grid-editable-row .admin__field-control._error .admin__control-text{border-color:#ee7d7d}.data-grid-editable-row .admin__field-control._focus:after{display:none}.data-grid-editable-row .admin__field-error{bottom:100%;box-shadow:1px 1px 5px rgba(0,0,0,.5);left:0;margin:0 auto 1.5rem;max-width:32rem;position:absolute;right:0}.data-grid-editable-row .admin__field-error:after,.data-grid-editable-row .admin__field-error:before{border-style:solid;content:'';left:50%;position:absolute;top:100%}.data-grid-editable-row .admin__field-error:after{border-color:#fffbbb transparent transparent;border-width:10px 10px 0;margin-left:-10px;z-index:1}.data-grid-editable-row .admin__field-error:before{border-color:#ee7d7d transparent transparent;border-width:11px 12px 0;margin-left:-12px}.data-grid-bulk-edit-panel .admin__field-label-vertical{display:block;font-size:1.2rem;margin-bottom:.5rem;text-align:left}.data-grid-row-changed{cursor:default;display:block;opacity:.5;position:relative;width:100%;z-index:1}.data-grid-row-changed:after{content:'\e631';display:inline-block}.data-grid-row-changed .data-grid-row-changed-tooltip{background:#f1f1f1;border:1px solid #f1f1f1;border-radius:1px;bottom:100%;box-shadow:0 3px 9px 0 rgba(0,0,0,.3);display:none;font-weight:400;line-height:1.36;margin-bottom:1.5rem;padding:1rem;position:absolute;right:-1rem;text-transform:none;width:27rem;word-break:normal;z-index:2}.data-grid-row-changed._changed{opacity:1;z-index:3}.data-grid-row-changed._changed:hover .data-grid-row-changed-tooltip{display:block}.data-grid-row-changed._changed:hover:before{background:#f1f1f1;border:1px solid #f1f1f1;bottom:100%;box-shadow:4px 4px 3px -1px rgba(0,0,0,.15);content:'';display:block;height:1.6rem;left:50%;margin:0 0 .7rem -.8rem;position:absolute;-ms-transform:rotate(45deg);transform:rotate(45deg);width:1.6rem;z-index:3}.ie9 .data-grid-row-changed._changed:hover:before{display:none}.admin__data-grid-outer-wrap .data-grid-checkbox-cell{overflow:hidden}.admin__data-grid-outer-wrap .data-grid-checkbox-cell-inner{position:relative}.admin__data-grid-outer-wrap .data-grid-checkbox-cell-inner:before{bottom:0;content:'';height:500%;left:0;position:absolute;right:0;top:0}.admin__data-grid-wrap-static .data-grid-checkbox-cell:hover{cursor:pointer}.admin__data-grid-wrap-static .data-grid-checkbox-cell-inner{margin:1.1rem 1.8rem .9rem;padding:0}.adminhtml-cms-hierarchy-index .admin__data-grid-wrap-static .data-grid-actions-cell:first-child{padding:0}.adminhtml-export-index .admin__data-grid-wrap-static .data-grid-checkbox-cell-inner{margin:0;padding:1.1rem 1.8rem 1.9rem}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child:before,.admin__control-file-label:before,.admin__control-multiselect,.admin__control-select,.admin__control-text,.admin__control-textarea,.selectmenu{-webkit-appearance:none;background-color:#fff;border:1px solid #adadad;border-radius:1px;box-shadow:none;color:#303030;font-size:1.4rem;font-weight:400;height:auto;line-height:1.36;padding:.6rem 1rem;transition:border-color .1s linear;vertical-align:baseline;width:auto}.admin__control-addon [class*=admin__control-][class]:hover~[class*=admin__addon-]:last-child:before,.admin__control-multiselect:hover,.admin__control-select:hover,.admin__control-text:hover,.admin__control-textarea:hover,.selectmenu:hover,.selectmenu:hover .selectmenu-toggle:before{border-color:#878787}.admin__control-addon [class*=admin__control-][class]:focus~[class*=admin__addon-]:last-child:before,.admin__control-file:active+.admin__control-file-label:before,.admin__control-file:focus+.admin__control-file-label:before,.admin__control-multiselect:focus,.admin__control-select:focus,.admin__control-text:focus,.admin__control-textarea:focus,.selectmenu._focus,.selectmenu._focus .selectmenu-toggle:before{border-color:#007bdb;box-shadow:none;outline:0}.admin__control-addon [class*=admin__control-][class][disabled]~[class*=admin__addon-]:last-child:before,.admin__control-file[disabled]+.admin__control-file-label:before,.admin__control-multiselect[disabled],.admin__control-select[disabled],.admin__control-text[disabled],.admin__control-textarea[disabled]{background-color:#e9e9e9;border-color:#adadad;color:#303030;cursor:not-allowed;opacity:.5}.admin__field-row[class]>.admin__field-control,.admin__fieldset>.admin__field.admin__field-wide[class]>.admin__field-control{clear:left;float:none;text-align:left;width:auto}.admin__field-row[class]:not(.admin__field-option)>.admin__field-label,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option)>.admin__field-label{display:block;line-height:1.4rem;margin-bottom:.86rem;margin-top:-.14rem;text-align:left;width:auto}.admin__field-row[class]:not(.admin__field-option)>.admin__field-label:before,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option)>.admin__field-label:before{display:none}.admin__field-row[class]:not(.admin__field-option)._required>.admin__field-label span,.admin__field-row[class]:not(.admin__field-option).required>.admin__field-label span,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option)._required>.admin__field-label span,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option).required>.admin__field-label span{padding-left:1.5rem}.admin__field-row[class]:not(.admin__field-option)._required>.admin__field-label span:after,.admin__field-row[class]:not(.admin__field-option).required>.admin__field-label span:after,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option)._required>.admin__field-label span:after,.admin__fieldset>.admin__field.admin__field-wide[class]:not(.admin__field-option).required>.admin__field-label span:after{left:0;margin-left:30px}.admin__legend{font-size:1.8rem;font-weight:600;margin-bottom:3rem}.admin__control-checkbox,.admin__control-radio{cursor:pointer;opacity:.01;overflow:hidden;position:absolute;vertical-align:top}.admin__control-checkbox:after,.admin__control-radio:after{display:none}.admin__control-checkbox+label,.admin__control-radio+label{cursor:pointer;display:inline-block}.admin__control-checkbox+label:before,.admin__control-radio+label:before{background-color:#fff;border:1px solid #adadad;color:transparent;float:left;height:1.6rem;text-align:center;vertical-align:top;width:1.6rem}.admin__control-checkbox+.admin__field-label,.admin__control-radio+.admin__field-label{padding-left:2.6rem}.admin__control-checkbox+.admin__field-label:before,.admin__control-radio+.admin__field-label:before{margin:1px 1rem 0 -2.6rem}.admin__control-checkbox:checked+label:before,.admin__control-radio:checked+label:before{color:#514943}.admin__control-checkbox.disabled+label,.admin__control-checkbox[disabled]+label,.admin__control-radio.disabled+label,.admin__control-radio[disabled]+label{color:#303030;cursor:default;opacity:.5}.admin__control-checkbox.disabled+label:before,.admin__control-checkbox[disabled]+label:before,.admin__control-radio.disabled+label:before,.admin__control-radio[disabled]+label:before{background-color:#e9e9e9;border-color:#adadad;cursor:default}._keyfocus .admin__control-checkbox:not(.disabled):focus+label:before,._keyfocus .admin__control-checkbox:not([disabled]):focus+label:before,._keyfocus .admin__control-radio:not(.disabled):focus+label:before,._keyfocus .admin__control-radio:not([disabled]):focus+label:before{border-color:#007bdb}.admin__control-checkbox:not(.disabled):hover+label:before,.admin__control-checkbox:not([disabled]):hover+label:before,.admin__control-radio:not(.disabled):hover+label:before,.admin__control-radio:not([disabled]):hover+label:before{border-color:#878787}.admin__control-radio+label:before{border-radius:1.6rem;content:'';transition:border-color .1s linear,color .1s ease-in}.admin__control-radio.admin__control-radio+label:before{line-height:140%}.admin__control-radio:checked+label{position:relative}.admin__control-radio:checked+label:after{background-color:#514943;border-radius:50%;content:'';height:10px;left:3px;position:absolute;top:4px;width:10px}.admin__control-radio:checked:not(.disabled):hover,.admin__control-radio:checked:not(.disabled):hover+label,.admin__control-radio:checked:not([disabled]):hover,.admin__control-radio:checked:not([disabled]):hover+label{cursor:default}.admin__control-radio:checked:not(.disabled):hover+label:before,.admin__control-radio:checked:not([disabled]):hover+label:before{border-color:#adadad}.admin__control-checkbox+label:before{border-radius:1px;content:'';font-size:0;transition:font-size .1s ease-out,color .1s ease-out,border-color .1s linear}.admin__control-checkbox:checked+label:before{content:'\e62d';font-size:1.1rem;line-height:125%}.admin__control-checkbox:not(:checked)._indeterminate+label:before,.admin__control-checkbox:not(:checked):indeterminate+label:before{color:#514943;content:'-';font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.4rem;font-weight:700}input[type=checkbox].admin__control-checkbox,input[type=radio].admin__control-checkbox{margin:0;position:absolute}.admin__control-text{min-width:4rem}.admin__control-select{-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none;background-image:url(../images/arrows-bg.svg),linear-gradient(#e3e3e3,#e3e3e3),linear-gradient(#adadad,#adadad);background-position:calc(100% - 12px) -34px,100%,calc(100% - 3.2rem) 0;background-size:auto,3.2rem 100%,1px 100%;background-repeat:no-repeat;max-width:100%;min-width:8.5rem;padding-bottom:.5rem;padding-right:4.4rem;padding-top:.5rem;transition:border-color .1s linear}.admin__control-select:hover{border-color:#878787;cursor:pointer}.admin__control-select:focus{background-image:url(../images/arrows-bg.svg),linear-gradient(#e3e3e3,#e3e3e3),linear-gradient(#007bdb,#007bdb);background-position:calc(100% - 12px) 13px,100%,calc(100% - 3.2rem) 0;border-color:#007bdb}.admin__control-select::-ms-expand{display:none}.ie9 .admin__control-select{background-image:none;padding-right:1rem}option:empty{display:none}.admin__control-multiselect{height:auto;max-width:100%;min-width:15rem;overflow:auto;padding:0;resize:both}.admin__control-multiselect optgroup,.admin__control-multiselect option{padding:.5rem 1rem}.admin__control-file-wrapper{display:inline-block;padding:.5rem 1rem;position:relative;z-index:1}.admin__control-file-label:before{content:'';left:0;position:absolute;top:0;width:100%;z-index:0}.admin__control-file{background:0 0;border:0;padding-top:.7rem;position:relative;width:auto;z-index:1}.admin__control-support-text{border:1px solid transparent;display:inline-block;font-size:1.4rem;line-height:1.36;padding-bottom:.6rem;padding-top:.6rem}.admin__control-support-text+[class*=admin__control-],[class*=admin__control-]+.admin__control-support-text{margin-left:.7rem}.admin__control-service{float:left;margin:.8rem 0 0 3rem}.admin__control-textarea{height:8.48rem;line-height:1.18;padding-top:.8rem;resize:vertical}.admin__control-addon{-ms-flex-direction:row;flex-direction:row;display:inline-flex;-ms-flex-flow:row nowrap;flex-flow:row nowrap;position:relative;width:100%;z-index:1}.admin__control-addon>[class*=admin__addon-],.admin__control-addon>[class*=admin__control-]{-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0;position:relative;z-index:1}.admin__control-addon .admin__control-select{width:auto}.admin__control-addon .admin__control-text{margin:.1rem;padding:.5rem .9rem;width:100%}.admin__control-addon [class*=admin__control-][class]{appearence:none;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-order:1;order:1;-ms-flex-negative:1;flex-shrink:1;background-color:transparent;border-color:transparent;box-shadow:none;vertical-align:top}.admin__control-addon [class*=admin__control-][class]+[class*=admin__control-]{border-left-color:#adadad}.admin__control-addon [class*=admin__control-][class] :focus{box-shadow:0}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child{padding-left:1rem;position:static!important;z-index:0}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child>*{position:relative;vertical-align:top;z-index:1}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child:empty{padding:0}.admin__control-addon [class*=admin__control-][class]~[class*=admin__addon-]:last-child:before{bottom:0;box-sizing:border-box;content:'';left:0;position:absolute;top:0;width:100%;z-index:-1}.admin__addon-prefix,.admin__addon-suffix{border:0;box-sizing:border-box;color:#858585;display:inline-block;font-size:1.4rem;font-weight:400;height:3.2rem;line-height:3.2rem;padding:0}.admin__addon-suffix{-ms-flex-order:3;order:3}.admin__addon-suffix:last-child{padding-right:1rem}.admin__addon-prefix{-ms-flex-order:0;order:0}.ie9 .admin__control-addon:after{clear:both;content:'';display:block;height:0;overflow:hidden}.ie9 .admin__addon{min-width:0;overflow:hidden;text-align:right;white-space:nowrap;width:auto}.ie9 .admin__addon [class*=admin__control-]{display:inline}.ie9 .admin__addon-prefix{float:left}.ie9 .admin__addon-suffix{float:right}.admin__control-collapsible{width:100%}.admin__control-collapsible ._dragged .admin__collapsible-block-wrapper .admin__collapsible-title{background:#d0d0d0}.admin__control-collapsible ._dragover-bottom .admin__collapsible-block-wrapper:before,.admin__control-collapsible ._dragover-top .admin__collapsible-block-wrapper:before{background:#008bdb;content:'';display:block;height:3px;left:0;position:absolute;right:0}.admin__control-collapsible ._dragover-top .admin__collapsible-block-wrapper:before{top:-3px}.admin__control-collapsible ._dragover-bottom .admin__collapsible-block-wrapper:before{bottom:-3px}.admin__control-collapsible .admin__collapsible-block-wrapper.fieldset-wrapper{border:0;margin:0;position:relative}.admin__control-collapsible .admin__collapsible-block-wrapper.fieldset-wrapper .fieldset-wrapper-title{background:#f8f8f8;border:2px solid #ccc}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .admin__collapsible-title{font-size:1.4rem;font-weight:400;line-height:1;padding:1.6rem 4rem 1.6rem 3.8rem}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .admin__collapsible-title:before{left:1rem;right:auto;top:1.4rem}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete{background-color:transparent;border-color:transparent;box-shadow:none;padding:0;position:absolute;right:1rem;top:1.4rem}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete:hover{background-color:transparent;border-color:transparent;box-shadow:none}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete:before{content:'\e630';font-size:2rem}.admin__control-collapsible .admin__collapsible-block-wrapper .fieldset-wrapper-title .action-delete>span{display:none}.admin__control-collapsible .admin__collapsible-content{background-color:#fff;margin-bottom:1rem}.admin__control-collapsible .admin__collapsible-content>.fieldset-wrapper{border:1px solid #ccc;margin-top:-1px;padding:1rem}.admin__control-collapsible .admin__collapsible-content .admin__fieldset{padding:0}.admin__control-collapsible .admin__collapsible-content .admin__field:last-child{margin-bottom:0}.admin__control-table-wrapper{max-width:100%;overflow-x:auto;overflow-y:hidden}.admin__control-table{width:100%}.admin__control-table thead{background-color:transparent}.admin__control-table tbody td{vertical-align:top}.admin__control-table tfoot th{padding-bottom:1.3rem}.admin__control-table tfoot th.validation{padding-bottom:0;padding-top:0}.admin__control-table tfoot td{border-top:1px solid #fff}.admin__control-table tfoot .admin__control-table-pagination{float:right;padding-bottom:0}.admin__control-table tfoot .action-previous{margin-right:.5rem}.admin__control-table tfoot .action-next{margin-left:.9rem}.admin__control-table tr:last-child td{border-bottom:none}.admin__control-table tr._dragover-top td{box-shadow:inset 0 3px 0 0 #008bdb}.admin__control-table tr._dragover-bottom td{box-shadow:inset 0 -3px 0 0 #008bdb}.admin__control-table tr._dragged td,.admin__control-table tr._dragged th{background:#d0d0d0}.admin__control-table td,.admin__control-table th{background-color:#efefef;border:0;border-bottom:1px solid #fff;padding:1.3rem 1rem 1.3rem 0;text-align:left;vertical-align:top}.admin__control-table td:first-child,.admin__control-table th:first-child{padding-left:1rem}.admin__control-table td>.admin__control-select,.admin__control-table td>.admin__control-text,.admin__control-table th>.admin__control-select,.admin__control-table th>.admin__control-text{width:100%}.admin__control-table td._hidden,.admin__control-table th._hidden{display:none}.admin__control-table td._fit,.admin__control-table th._fit{width:1px}.admin__control-table th{color:#303030;font-size:1.4rem;font-weight:600;vertical-align:bottom}.admin__control-table th._required span:after{color:#eb5202;content:'*'}.admin__control-table .control-table-actions-th{white-space:nowrap}.admin__control-table .control-table-actions-cell{padding-top:1.8rem;text-align:center;width:1%}.admin__control-table .control-table-options-th{text-align:center;width:10rem}.admin__control-table .control-table-options-cell{text-align:center}.admin__control-table .control-table-text{line-height:3.2rem}.admin__control-table .col-draggable{padding-top:2.2rem;width:1%}.admin__control-table .action-delete{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.admin__control-table .action-delete:hover{background-color:transparent;border-color:transparent;box-shadow:none}.admin__control-table .action-delete:before{content:'\e630';font-size:2rem}.admin__control-table .action-delete>span{display:none}.admin__control-table .draggable-handle{padding:0}.admin__control-table._dragged{outline:#007bdb solid 1px}.admin__control-table-action{background-color:#efefef;border-top:1px solid #fff;padding:1.3rem 1rem}.admin__dynamic-rows._dragged{opacity:.95;position:absolute;z-index:999}.admin__dynamic-rows.admin__control-table .admin__control-fields>.admin__field{border:0;padding:0}.admin__dynamic-rows td>.admin__field{border:0;margin:0;padding:0}.admin__control-table-pagination{padding-bottom:1rem}.admin__control-table-pagination .admin__data-grid-pager{float:right}.admin__field-tooltip{display:inline-block;margin-top:.5rem;max-width:45px;overflow:visible;vertical-align:top;width:0}.admin__field-tooltip:hover{position:relative;z-index:500}.admin__field-option .admin__field-tooltip{margin-top:.5rem}.admin__field-tooltip .admin__field-tooltip-action{margin-left:2rem;position:relative;z-index:2;display:inline-block;text-decoration:none}.admin__field-tooltip .admin__field-tooltip-action:before{-webkit-font-smoothing:antialiased;font-size:2.2rem;line-height:1;color:#514943;content:'\e633';font-family:Icons;vertical-align:middle;display:inline-block;font-weight:400;overflow:hidden;speak:none;text-align:center}.admin__field-tooltip .admin__control-text:focus+.admin__field-tooltip-content,.admin__field-tooltip:hover .admin__field-tooltip-content{display:block}.admin__field-tooltip .admin__field-tooltip-content{bottom:3.8rem;display:none;right:-2.3rem}.admin__field-tooltip .admin__field-tooltip-content:after,.admin__field-tooltip .admin__field-tooltip-content:before{border:1.6rem solid transparent;height:0;width:0;border-top-color:#afadac;content:'';display:block;position:absolute;right:2rem;top:100%;z-index:3}.admin__field-tooltip .admin__field-tooltip-content:after{border-top-color:#fffbbb;margin-top:-1px;z-index:4}.abs-admin__field-tooltip-content,.admin__field-tooltip .admin__field-tooltip-content{box-shadow:0 2px 8px 0 rgba(0,0,0,.3);background:#fffbbb;border:1px solid #afadac;border-radius:1px;padding:1.5rem 2.5rem;position:absolute;width:32rem;z-index:1}.admin__field-fallback-reset{font-size:1.25rem;white-space:nowrap;width:30px}.admin__field-fallback-reset>span{margin-left:.5rem;position:relative}.admin__field-fallback-reset:active{-ms-transform:scale(0.98);transform:scale(0.98)}.admin__field-fallback-reset:before{transition:color .1s linear;content:'\e642';font-size:1.3rem;margin-left:.5rem}.admin__field-fallback-reset:hover{cursor:pointer;text-decoration:none}.admin__field-fallback-reset:focus{background:0 0}.abs-field-size-x-small,.abs-field-sizes.admin__field-x-small>.admin__field-control,.admin__field.admin__field-x-small>.admin__field-control,.admin__fieldset>.admin__field.admin__field-x-small>.admin__field-control,[class*=admin__control-grouped]>.admin__field.admin__field-x-small>.admin__field-control{width:8rem}.abs-field-size-small,.abs-field-sizes.admin__field-small>.admin__field-control,.admin__control-grouped-date>.admin__field-date.admin__field>.admin__field-control,.admin__field.admin__field-small>.admin__field-control,.admin__fieldset>.admin__field.admin__field-small>.admin__field-control,[class*=admin__control-grouped]>.admin__field.admin__field-small>.admin__field-control{width:15rem}.abs-field-size-medium,.abs-field-sizes.admin__field-medium>.admin__field-control,.admin__field.admin__field-medium>.admin__field-control,.admin__fieldset>.admin__field.admin__field-medium>.admin__field-control,[class*=admin__control-grouped]>.admin__field.admin__field-medium>.admin__field-control{width:34rem}.abs-field-size-large,.abs-field-sizes.admin__field-large>.admin__field-control,.admin__field.admin__field-large>.admin__field-control,.admin__fieldset>.admin__field.admin__field-large>.admin__field-control,[class*=admin__control-grouped]>.admin__field.admin__field-large>.admin__field-control{width:64rem}.abs-field-no-label,.admin__field-group-additional,.admin__field-no-label,.admin__fieldset>.admin__field.admin__field-no-label>.admin__field-control{margin-left:calc((100%) * .25 + 30px)}.admin__fieldset{border:0;margin:0;min-width:0;padding:0}.admin__fieldset .fieldset-wrapper.admin__fieldset-section>.fieldset-wrapper-title{padding-left:1rem}.admin__fieldset .fieldset-wrapper.admin__fieldset-section>.fieldset-wrapper-title strong{font-size:1.7rem;font-weight:600}.admin__fieldset .fieldset-wrapper.admin__fieldset-section .admin__fieldset-wrapper-content>.admin__fieldset{padding-top:1rem}.admin__fieldset .fieldset-wrapper.admin__fieldset-section:last-child .admin__fieldset-wrapper-content>.admin__fieldset{padding-bottom:0}.admin__fieldset>.admin__field{border:0;margin:0 0 0 -30px;padding:0}.admin__fieldset>.admin__field:after{clear:both;content:'';display:table}.admin__fieldset>.admin__field>.admin__field-control{width:calc((100%) * .5 - 30px);float:left;margin-left:30px}.admin__fieldset>.admin__field>.admin__field-label{width:calc((100%) * .25 - 30px);float:left;margin-left:30px}.admin__fieldset>.admin__field.admin__field-no-label>.admin__field-label{display:none}.admin__fieldset>.admin__field+.admin__field._empty._no-header{margin-top:-3rem}.admin__fieldset-product-websites{position:relative;z-index:300}.admin__fieldset-note{margin-bottom:2rem}.admin__form-field{border:0;margin:0;padding:0}.admin__field-control .admin__control-text,.admin__field-control .admin__control-textarea,.admin__form-field-control .admin__control-text,.admin__form-field-control .admin__control-textarea{width:100%}.admin__field-label{color:#303030;cursor:pointer;margin:0;text-align:right}.admin__field-label+br{display:none}.admin__field:not(.admin__field-option)>.admin__field-label{font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.4rem;font-weight:600;line-height:3.2rem;padding:0;white-space:nowrap}.admin__field:not(.admin__field-option)>.admin__field-label:before{opacity:0;visibility:hidden;content:'.';margin-left:-7px;overflow:hidden}.admin__field:not(.admin__field-option)>.admin__field-label span{display:inline-block;line-height:1.2;vertical-align:middle;white-space:normal}.admin__field:not(.admin__field-option)>.admin__field-label span[data-config-scope]{position:relative}._required>.admin__field-label>span:after,.required>.admin__field-label>span:after{color:#eb5202;content:'*';display:inline-block;font-size:1.6rem;font-weight:500;line-height:1;margin-left:10px;margin-top:.2rem;position:absolute;z-index:1}._disabled>.admin__field-label{color:#999;cursor:default}.admin__field{margin-bottom:0}.admin__field+.admin__field{margin-top:1.5rem}.admin__field:not(.admin__field-option)~.admin__field-option{margin-top:.5rem}.admin__field.admin__field-option~.admin__field-option{margin-top:.9rem}.admin__field~.admin__field-option:last-child{margin-bottom:.8rem}.admin__fieldset>.admin__field{margin-bottom:3rem;position:relative}.admin__field legend.admin__field-label{opacity:0}.admin__field[data-config-scope]:before{color:gray;content:attr(data-config-scope);display:inline-block;font-size:1.2rem;left:calc((100%) * .75 - 30px);line-height:3.2rem;margin-left:60px;position:absolute;width:calc((100%) * .25 - 30px)}.admin__field-control .admin__field[data-config-scope]:nth-child(n+2):before{content:''}.admin__field._error .admin__field-control [class*=admin__addon-]:before,.admin__field._error .admin__field-control [class*=admin__control-] [class*=admin__addon-]:before,.admin__field._error .admin__field-control>[class*=admin__control-]{border-color:#e22626}.admin__field._disabled,.admin__field._disabled:hover{box-shadow:inherit;cursor:inherit;opacity:1;outline:inherit}.admin__field._hidden{display:none}.admin__field-control+.admin__field-control{margin-top:1.5rem}.admin__field-control._with-tooltip>.admin__control-addon,.admin__field-control._with-tooltip>.admin__control-select,.admin__field-control._with-tooltip>.admin__control-text,.admin__field-control._with-tooltip>.admin__control-textarea,.admin__field-control._with-tooltip>.admin__field-option{max-width:calc(100% - 45px - 4px)}.admin__field-control._with-tooltip .admin__field-tooltip{width:auto}.admin__field-control._with-tooltip .admin__field-option{display:inline-block}.admin__field-control._with-reset>.admin__control-addon,.admin__field-control._with-reset>.admin__control-text,.admin__field-control._with-reset>.admin__control-textarea{width:calc(100% - 30px - .5rem - 4px)}.admin__field-control._with-reset .admin__field-fallback-reset{margin-left:.5rem;margin-top:1rem;vertical-align:top}.admin__field-control._with-reset._with-tooltip>.admin__control-addon,.admin__field-control._with-reset._with-tooltip>.admin__control-text,.admin__field-control._with-reset._with-tooltip>.admin__control-textarea{width:calc(100% - 30px - .5rem - 45px - 8px)}.admin__fieldset>.admin__field-collapsible{margin-bottom:0}.admin__fieldset>.admin__field-collapsible .admin__field-control{border-top:1px solid #ccc;display:block;font-size:1.7rem;font-weight:700;padding:1.7rem 0;width:calc(97%)}.admin__fieldset>.admin__field-collapsible .admin__field-option{padding-top:0}.admin__field-collapsible+div{margin-top:2.5rem}.admin__field-collapsible .admin__control-radio+label:before{height:1.8rem;width:1.8rem}.admin__field-collapsible .admin__control-radio:checked+label:after{left:4px;top:5px}.admin__field-error{background:#fffbbb;border:1px solid #ee7d7d;box-sizing:border-box;color:#555;display:block;font-size:1.2rem;font-weight:400;line-height:1.2;margin:.2rem 0 0;padding:.8rem 1rem .9rem}.admin__field-note{color:#303030;font-size:1.2rem;margin:10px 0 0;padding:0}.admin__additional-info{padding-top:1rem}.admin__field-option{padding-top:.7rem}.admin__field-option .admin__field-label{text-align:left}.admin__field-control>.admin__field-option:nth-child(1):nth-last-child(2),.admin__field-control>.admin__field-option:nth-child(2):nth-last-child(1){display:inline-block}.admin__field-control>.admin__field-option:nth-child(1):nth-last-child(2)+.admin__field-option,.admin__field-control>.admin__field-option:nth-child(2):nth-last-child(1)+.admin__field-option{display:inline-block;margin-left:41px;margin-top:0}.admin__field-control>.admin__field-option:nth-child(1):nth-last-child(2)+.admin__field-option:before,.admin__field-control>.admin__field-option:nth-child(2):nth-last-child(1)+.admin__field-option:before{background:#cacaca;content:'';display:inline-block;height:20px;margin-left:-20px;position:absolute;width:1px}.admin__field-value{display:inline-block;padding-top:.7rem}.admin__field-service{padding-top:1rem}.admin__control-fields>.admin__field:first-child,[class*=admin__control-grouped]>.admin__field:first-child{position:static}.admin__control-fields>.admin__field:first-child>.admin__field-label,[class*=admin__control-grouped]>.admin__field:first-child>.admin__field-label{width:calc((100%) * .25 - 30px);float:left;margin-left:30px;background:#fff;cursor:pointer;left:0;position:absolute;top:0}.admin__control-fields>.admin__field:first-child>.admin__field-label span:before,[class*=admin__control-grouped]>.admin__field:first-child>.admin__field-label span:before{display:block}.admin__control-fields>.admin__field._disabled>.admin__field-label,[class*=admin__control-grouped]>.admin__field._disabled>.admin__field-label{cursor:default}.admin__control-fields>.admin__field>.admin__field-label span:before,[class*=admin__control-grouped]>.admin__field>.admin__field-label span:before{display:none}.admin__control-fields .admin__field-label~.admin__field-control{width:100%}.admin__control-fields .admin__field-option{padding-top:0}[class*=admin__control-grouped]{box-sizing:border-box;display:table;width:100%}[class*=admin__control-grouped]>.admin__field{display:table-cell;vertical-align:top}[class*=admin__control-grouped]>.admin__field>.admin__field-control{float:none;width:100%}[class*=admin__control-grouped]>.admin__field.admin__field-default,[class*=admin__control-grouped]>.admin__field.admin__field-large,[class*=admin__control-grouped]>.admin__field.admin__field-medium,[class*=admin__control-grouped]>.admin__field.admin__field-small,[class*=admin__control-grouped]>.admin__field.admin__field-x-small{width:1px}[class*=admin__control-grouped]>.admin__field.admin__field-default+.admin__field:last-child,[class*=admin__control-grouped]>.admin__field.admin__field-large+.admin__field:last-child,[class*=admin__control-grouped]>.admin__field.admin__field-medium+.admin__field:last-child,[class*=admin__control-grouped]>.admin__field.admin__field-small+.admin__field:last-child,[class*=admin__control-grouped]>.admin__field.admin__field-x-small+.admin__field:last-child{width:auto}[class*=admin__control-grouped]>.admin__field:nth-child(n+2){padding-left:20px}.admin__control-group-equal{table-layout:fixed}.admin__control-group-equal>.admin__field{width:50%}.admin__field-control-group{margin-top:.8rem}.admin__field-control-group>.admin__field{padding:0}.admin__control-grouped-date>.admin__field-date{white-space:nowrap;width:1px}.admin__control-grouped-date>.admin__field-date.admin__field>.admin__field-control{float:left;position:relative}.admin__control-grouped-date>.admin__field-date+.admin__field:last-child{width:auto}.admin__control-grouped-date>.admin__field-date+.admin__field-date>.admin__field-label{float:left;padding-right:20px}.admin__control-grouped-date .ui-datepicker-trigger{left:100%;top:0}.admin__field-group-columns.admin__field-control.admin__control-grouped{width:calc((100%) * 1 - 30px);float:left;margin-left:30px}.admin__field-group-columns>.admin__field:first-child>.admin__field-label{float:none;margin:0;opacity:1;position:static;text-align:left}.admin__field-group-columns .admin__control-select{width:100%}.admin__field-group-additional{clear:both}.admin__field-group-additional .action-advanced{margin-top:1rem}.admin__field-group-additional .action-secondary{width:100%}.admin__field-group-show-label{white-space:nowrap}.admin__field-group-show-label>.admin__field-control,.admin__field-group-show-label>.admin__field-label{display:inline-block;vertical-align:top}.admin__field-group-show-label>.admin__field-label{margin-right:20px}.admin__field-complex{margin:1rem 0 3rem;padding-left:1rem}.admin__field:not(._hidden)+.admin__field-complex{margin-top:3rem}.admin__field-complex .admin__field-complex-title{clear:both;color:#303030;font-size:1.7rem;font-weight:600;letter-spacing:.025em;margin-bottom:1rem}.admin__field-complex .admin__field-complex-elements{float:right;max-width:40%}.admin__field-complex .admin__field-complex-elements button{margin-left:1rem}.admin__field-complex .admin__field-complex-content{max-width:60%;overflow:hidden}.admin__field-complex .admin__field-complex-text{margin-left:-1rem}.admin__field-complex+.admin__field._empty._no-header{margin-top:-3rem}.admin__legend{float:left;position:static;width:100%}.admin__legend+br{clear:left;display:block;height:0;overflow:hidden}.message{margin-bottom:3rem}.message-icon-top:before{margin-top:0;top:1.8rem}.nav{background-color:#f8f8f8;border-bottom:1px solid #e3e3e3;border-top:1px solid #e3e3e3;display:none;margin-bottom:3rem;padding:2.2rem 1.5rem 0 0}.nav .btn-group,.nav-bar-outer-actions{float:right;margin-bottom:1.7rem}.nav .btn-group .btn-wrap,.nav-bar-outer-actions .btn-wrap{float:right;margin-left:.5rem;margin-right:.5rem}.nav .btn-group .btn-wrap .btn,.nav-bar-outer-actions .btn-wrap .btn{padding-left:.5rem;padding-right:.5rem}.nav-bar-outer-actions{margin-top:-10.6rem;padding-right:1.5rem}.btn-wrap-try-again{width:9.5rem}.btn-wrap-next,.btn-wrap-prev{width:8.5rem}.nav-bar{counter-reset:i;float:left;margin:0 1rem 1.7rem 0;padding:0;position:relative;white-space:nowrap}.nav-bar:before{background-color:#d4d4d4;background-repeat:repeat-x;background-image:linear-gradient(to bottom,#d1d1d1 0,#d4d4d4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#d1d1d1', endColorstr='#d4d4d4', GradientType=0);border-bottom:1px solid #d9d9d9;border-top:1px solid #bfbfbf;content:'';height:1rem;left:5.15rem;position:absolute;right:5.15rem;top:.7rem}.nav-bar>li{display:inline-block;font-size:0;position:relative;vertical-align:top;width:10.3rem}.nav-bar>li:first-child:after{display:none}.nav-bar>li:after{background-color:#514943;content:'';height:.5rem;left:calc(-50% + .25rem);position:absolute;right:calc(50% + .7rem);top:.9rem}.nav-bar>li.disabled:before,.nav-bar>li.ui-state-disabled:before{bottom:0;content:'';left:0;position:absolute;right:0;top:0;z-index:1}.nav-bar>li.active~li:after,.nav-bar>li.ui-state-active~li:after{display:none}.nav-bar>li.active~li a:after,.nav-bar>li.ui-state-active~li a:after{background-color:transparent;border-color:transparent;color:#a6a6a6}.nav-bar>li.active a,.nav-bar>li.ui-state-active a{color:#000}.nav-bar>li.active a:hover,.nav-bar>li.ui-state-active a:hover{cursor:default}.nav-bar>li.active a:after,.nav-bar>li.ui-state-active a:after{background-color:#fff;content:''}.nav-bar a{color:#514943;display:block;font-size:1.2rem;font-weight:600;line-height:1.2;overflow:hidden;padding:3rem .5em 0;position:relative;text-align:center;text-overflow:ellipsis}.nav-bar a:hover{text-decoration:none}.nav-bar a:after{background-color:#514943;border:.4rem solid #514943;border-radius:100%;color:#fff;content:counter(i);counter-increment:i;height:1.5rem;left:50%;line-height:.6;margin-left:-.8rem;position:absolute;right:auto;text-align:center;top:.4rem;width:1.5rem}.nav-bar a:before{background-color:#d6d6d6;border:1px solid transparent;border-bottom-color:#d9d9d9;border-radius:100%;border-top-color:#bfbfbf;content:'';height:2.3rem;left:50%;line-height:1;margin-left:-1.2rem;position:absolute;top:0;width:2.3rem}.tooltip{display:block;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:1.19rem;font-weight:400;line-height:1.4;opacity:0;position:absolute;visibility:visible;z-index:10}.tooltip.in{opacity:.9}.tooltip.top{margin-top:-4px;padding:8px 0}.tooltip.right{margin-left:4px;padding:0 8px}.tooltip.bottom{margin-top:4px;padding:8px 0}.tooltip.left{margin-left:-4px;padding:0 8px}.tooltip p:last-child{margin-bottom:0}.tooltip-inner{background-color:#fff;border:1px solid #adadad;border-radius:0;box-shadow:1px 1px 1px #ccc;color:#41362f;max-width:31rem;padding:.5em 1em;text-decoration:none}.tooltip-arrow,.tooltip-arrow:after{border:solid transparent;height:0;position:absolute;width:0}.tooltip-arrow:after{content:'';position:absolute}.tooltip.top .tooltip-arrow,.tooltip.top .tooltip-arrow:after{border-top-color:#949494;border-width:8px 8px 0;bottom:0;left:50%;margin-left:-8px}.tooltip.top-left .tooltip-arrow,.tooltip.top-left .tooltip-arrow:after{border-top-color:#949494;border-width:8px 8px 0;bottom:0;margin-bottom:-8px;right:8px}.tooltip.top-right .tooltip-arrow,.tooltip.top-right .tooltip-arrow:after{border-top-color:#949494;border-width:8px 8px 0;bottom:0;left:8px;margin-bottom:-8px}.tooltip.right .tooltip-arrow,.tooltip.right .tooltip-arrow:after{border-right-color:#949494;border-width:8px 8px 8px 0;left:1px;margin-top:-8px;top:50%}.tooltip.right .tooltip-arrow:after{border-right-color:#fff;border-width:6px 7px 6px 0;margin-left:0;margin-top:-6px}.tooltip.left .tooltip-arrow,.tooltip.left .tooltip-arrow:after{border-left-color:#949494;border-width:8px 0 8px 8px;margin-top:-8px;right:0;top:50%}.tooltip.bottom .tooltip-arrow,.tooltip.bottom .tooltip-arrow:after{border-bottom-color:#949494;border-width:0 8px 8px;left:50%;margin-left:-8px;top:0}.tooltip.bottom-left .tooltip-arrow,.tooltip.bottom-left .tooltip-arrow:after{border-bottom-color:#949494;border-width:0 8px 8px;margin-top:-8px;right:8px;top:0}.tooltip.bottom-right .tooltip-arrow,.tooltip.bottom-right .tooltip-arrow:after{border-bottom-color:#949494;border-width:0 8px 8px;left:8px;margin-top:-8px;top:0}.password-strength{display:block;margin:0 -.3rem 1em;white-space:nowrap}.password-strength.password-strength-too-short .password-strength-item:first-child,.password-strength.password-strength-weak .password-strength-item:first-child,.password-strength.password-strength-weak .password-strength-item:first-child+.password-strength-item{background-color:#e22626}.password-strength.password-strength-fair .password-strength-item:first-child,.password-strength.password-strength-fair .password-strength-item:first-child+.password-strength-item,.password-strength.password-strength-fair .password-strength-item:first-child+.password-strength-item+.password-strength-item{background-color:#ef672f}.password-strength.password-strength-good .password-strength-item:first-child,.password-strength.password-strength-good .password-strength-item:first-child+.password-strength-item,.password-strength.password-strength-good .password-strength-item:first-child+.password-strength-item+.password-strength-item,.password-strength.password-strength-good .password-strength-item:first-child+.password-strength-item+.password-strength-item+.password-strength-item,.password-strength.password-strength-strong .password-strength-item{background-color:#79a22e}.password-strength .password-strength-item{background-color:#ccc;display:inline-block;font-size:0;height:1.4rem;margin-right:.3rem;width:calc(20% - .6rem)}@keyframes progress-bar-stripes{from{background-position:4rem 0}to{background-position:0 0}}.progress{background-color:#fafafa;border:1px solid #ccc;clear:left;height:3rem;margin-bottom:3rem;overflow:hidden}.progress-bar{background-color:#79a22e;color:#fff;float:left;font-size:1.19rem;height:100%;line-height:3rem;text-align:center;transition:width .6s ease;width:0}.progress-bar.active{animation:progress-bar-stripes 2s linear infinite}.progress-bar-text-description{margin-bottom:1.6rem}.progress-bar-text-progress{text-align:right}.page-columns .page-inner-sidebar{margin:0 0 3rem}.page-header{margin-bottom:2.7rem;padding-bottom:2rem;position:relative}.page-header:before{border-bottom:1px solid #e3e3e3;bottom:0;content:'';display:block;height:1px;left:3rem;position:absolute;right:3rem}.container .page-header:before{content:normal}.page-header .message{margin-bottom:1.8rem}.page-header .message+.message{margin-top:-1.5rem}.page-header .admin__action-dropdown,.page-header .search-global-input{transition:none}.container .page-header{margin-bottom:0}.page-title-wrapper{margin-top:1.1rem}.container .page-title-wrapper{background:url(../../pub/images/logo.svg) no-repeat;min-height:41px;padding:4px 0 0 45px}.admin__menu .level-0:first-child>a{margin-top:1.6rem}.admin__menu .level-0:first-child>a:after{top:-1.6rem}.admin__menu .level-0:first-child._active>a:after{display:block}.admin__menu .level-0>a{padding-bottom:1.3rem;padding-top:1.3rem}.admin__menu .level-0>a:before{margin-bottom:.7rem}.admin__menu .item-home>a:before{content:'\e611';font-size:2.3rem;padding-top:-.1rem}.admin__menu .item-component>a:before{content:'\e612'}.admin__menu .item-extension>a:before{content:'\e647'}.admin__menu .item-upgrade>a:before{content:'\e614'}.admin__menu .item-system-config>a:before{content:'\e610'}.admin__menu .item-tools>a:before{content:'\e613'}.modal-sub-title{font-size:1.7rem;font-weight:600}.modal-connect-signin .modal-inner-wrap{max-width:80rem}@keyframes ngdialog-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes ngdialog-fadein{0%{opacity:0}100%{opacity:1}}.ngdialog{-webkit-overflow-scrolling:touch;bottom:0;box-sizing:border-box;left:0;overflow:auto;position:fixed;right:0;top:0;z-index:999}.ngdialog *,.ngdialog:after,.ngdialog:before{box-sizing:inherit}.ngdialog.ngdialog-disabled-animation *{animation:none!important}.ngdialog.ngdialog-closing .ngdialog-content,.ngdialog.ngdialog-closing .ngdialog-overlay{-webkit-animation:ngdialog-fadeout .5s;-webkit-backface-visibility:hidden;animation:ngdialog-fadeout .5s}.ngdialog-overlay{-webkit-animation:ngdialog-fadein .5s;-webkit-backface-visibility:hidden;animation:ngdialog-fadein .5s;background:rgba(0,0,0,.4);bottom:0;left:0;position:fixed;right:0;top:0}.ngdialog-content{-webkit-animation:ngdialog-fadein .5s;-webkit-backface-visibility:hidden;animation:ngdialog-fadein .5s}body.ngdialog-open{overflow:hidden}.component-indicator{border-radius:50%;cursor:help;display:inline-block;height:16px;text-align:center;vertical-align:middle;width:16px}.component-indicator::after,.component-indicator::before{background:#fff;display:block;opacity:0;position:absolute;transition:opacity .2s linear .1s;visibility:hidden}.component-indicator::before{border:1px solid #adadad;border-radius:1px;box-shadow:0 0 2px rgba(0,0,0,.4);content:attr(data-label);font-size:1.2rem;margin:30px 0 0 -10px;min-width:50px;padding:4px 5px}.component-indicator::after{border-color:#999;border-style:solid;border-width:1px 0 0 1px;box-shadow:-1px -1px 1px rgba(0,0,0,.1);content:'';height:10px;margin:9px 0 0 5px;-ms-transform:rotate(45deg);transform:rotate(45deg);width:10px}.component-indicator:hover::after,.component-indicator:hover::before{opacity:1;transition:opacity .2s linear;visibility:visible}.component-indicator span{display:block;height:16px;overflow:hidden;width:16px}.component-indicator span:before{content:'';display:block;font-family:Icons;font-size:16px;height:100%;line-height:16px;width:100%}.component-indicator._on{background:#79a22e}.component-indicator._off{background:#e22626}.component-indicator._off span:before{background:#fff;height:4px;margin:8px auto 20px;width:12px}.component-indicator._info{background:0 0}.component-indicator._info span{width:21px}.component-indicator._info span:before{color:#008bdb;content:'\e648';font-family:Icons;font-size:16px}.component-indicator._tooltip{background:0 0;margin:0 0 8px 5px}.component-indicator._tooltip a{width:21px}.component-indicator._tooltip a:hover{text-decoration:none}.component-indicator._tooltip a:before{color:#514943;content:'\e633';font-family:Icons;font-size:16px}.col-manager-item-name .data-grid-data{padding-left:5px}.col-manager-item-name .ng-hide+.data-grid-data{padding-left:24px}.col-manager-item-name ._hide-dependencies,.col-manager-item-name ._show-dependencies{cursor:pointer;padding-left:24px;position:relative}.col-manager-item-name ._hide-dependencies:before,.col-manager-item-name ._show-dependencies:before{display:block;font-family:Icons;font-size:12px;left:0;position:absolute;top:1px}.col-manager-item-name ._show-dependencies:before{content:'\e62b'}.col-manager-item-name ._hide-dependencies:before{content:'\e628'}.col-manager-item-name ._no-dependencies{padding-left:24px}.product-modules-block{font-size:1.2rem;padding:15px 0 0}.col-manager-item-name .product-modules-block{padding-left:1rem}.product-modules-descriprion,.product-modules-title{font-weight:700;margin:0 0 7px}.product-modules-list{font-size:1.1rem;list-style:none;margin:0}.col-manager-item-name .product-modules-list{margin-left:15px}.col-manager-item-name .product-modules-list li{padding:0 0 0 15px;position:relative}.product-modules-list li{margin:0 0 .5rem}.product-modules-list .component-indicator{height:10px;left:0;position:absolute;top:3px;width:10px}.module-summary{white-space:nowrap}.module-summary-title{font-size:2.1rem;margin-right:1rem}.app-updater .nav{display:block;margin-bottom:3.1rem;margin-top:-2.8rem}.app-updater .nav-bar-outer-actions{margin-top:1rem;padding-right:0}.app-updater .nav-bar-outer-actions .btn-wrap-cancel{margin-right:2.6rem}.main{padding-bottom:2rem;padding-top:3rem}.menu-wrapper .logo-static{pointer-events:none}.header{display:none}.header .logo{float:left;height:4.1rem;width:3.5rem}.header-title{font-size:2.8rem;letter-spacing:.02em;line-height:1.4;margin:2.5rem 0 3.5rem 5rem}.page-title{margin-bottom:1rem}.page-sub-title{font-size:2rem}.accent-box{margin-bottom:2rem}.accent-box .btn-prime{margin-top:1.5rem}.spinner.side{float:left;font-size:2.4rem;margin-left:2rem;margin-top:-5px}.page-landing{margin:7.6% auto 0;max-width:44rem;text-align:center}.page-landing .logo{height:5.6rem;margin-bottom:2rem;width:19.2rem}.page-landing .text-version{margin-bottom:3rem}.page-landing .text-welcome{margin-bottom:6.5rem}.page-landing .text-terms{margin-bottom:2.5rem;text-align:center}.page-landing .btn-submit,.page-license .license-text{margin-bottom:2rem}.page-license .page-license-footer{text-align:right}.readiness-check-item{margin-bottom:4rem;min-height:2.5rem}.readiness-check-item .spinner{float:left;font-size:2.5rem;margin:-.4rem 0 0 1.7rem}.readiness-check-title{font-size:1.4rem;font-weight:700;margin-bottom:.1rem;margin-left:5.7rem}.readiness-check-content{margin-left:5.7rem;margin-right:22rem;position:relative}.readiness-check-content .readiness-check-title{margin-left:0}.readiness-check-content .list{margin-top:-.3rem}.readiness-check-side{left:100%;padding-left:2.4rem;position:absolute;top:0;width:22rem}.readiness-check-side .side-title{margin-bottom:0}.readiness-check-icon{float:left;margin-left:1.7rem;margin-top:.3rem}.extensions-information{margin-bottom:5rem}.extensions-information h3{font-size:1.4rem;margin-bottom:1.3rem}.extensions-information .message{margin-bottom:2.5rem}.extensions-information .message:before{margin-top:0;top:1.8rem}.extensions-information .extensions-container{padding:0 2rem}.extensions-information .list{margin-bottom:1rem}.extensions-information .list select{cursor:pointer}.extensions-information .list select:disabled{background:#ccc;cursor:default}.extensions-information .list .extension-delete{font-size:1.7rem;padding-top:0}.delete-modal-wrap{padding:0 4% 4rem}.delete-modal-wrap h3{font-size:3.4rem;display:inline-block;font-weight:300;margin:0 0 2rem;padding:.9rem 0 0;vertical-align:top}.delete-modal-wrap .actions{padding:3rem 0 0}.page-web-configuration .form-el-insider-wrap{width:auto}.page-web-configuration .form-el-insider{width:15.4rem}.page-web-configuration .form-el-insider-input .form-el-input{width:16.5rem}.customize-your-store .advanced-modules-count,.customize-your-store .advanced-modules-select{padding-left:1.5rem}.customize-your-store .customize-your-store-advanced{min-width:0}.customize-your-store .message-error:before{margin-top:0;top:1.8rem}.customize-your-store .message-error a{color:#333;text-decoration:underline}.customize-your-store .message-error .form-label:before{background:#fff}.customize-your-store .customize-database-clean p{margin-top:2.5rem}.content-install{margin-bottom:2rem}.console{border:1px solid #ccc;font-family:'Courier New',Courier,monospace;font-weight:300;height:20rem;margin:1rem 0 2rem;overflow-y:auto;padding:1.5rem 2rem 2rem;resize:vertical}.console .text-danger{color:#e22626}.console .text-success{color:#090}.console .hidden{display:none}.content-success .btn-prime{margin-top:1.5rem}.jumbo-title{font-size:3.6rem}.jumbo-title .jumbo-icon{font-size:3.8rem;margin-right:.25em;position:relative;top:.15em}.install-database-clean{margin-top:4rem}.install-database-clean .btn{margin-right:1rem}.page-sub-title{margin-bottom:2.1rem;margin-top:3rem}.multiselect-custom{max-width:71.1rem}.content-install{margin-top:3.7rem}.home-page-inner-wrap{margin:0 auto;max-width:91rem}.setup-home-title{margin-bottom:3.9rem;padding-top:1.8rem;text-align:center}.setup-home-item{background-color:#fafafa;border:1px solid #ccc;color:#333;display:block;margin-bottom:2rem;margin-left:1.3rem;margin-right:1.3rem;min-height:30rem;padding:2rem;text-align:center}.setup-home-item:hover{border-color:#8c8c8c;color:#333;text-decoration:none;transition:border-color .1s linear}.setup-home-item:active{-ms-transform:scale(0.99);transform:scale(0.99)}.setup-home-item:before{display:block;font-size:7rem;margin-bottom:3.3rem;margin-top:4rem}.setup-home-item-component:before,.setup-home-item-extension:before{content:'\e612'}.setup-home-item-module:before{content:'\e647'}.setup-home-item-upgrade:before{content:'\e614'}.setup-home-item-configuration:before{content:'\e610'}.setup-home-item-title{display:block;font-size:1.8rem;letter-spacing:.025em;margin-bottom:1rem}.setup-home-item-description{display:block}.extension-manager-wrap{border:1px solid #bbb;margin:0 0 4rem}.extension-manager-account{font-size:2.1rem;display:inline-block;font-weight:400}.extension-manager-title{font-size:3.2rem;background-color:#f8f8f8;border-bottom:1px solid #e3e3e3;color:#41362f;font-weight:600;line-height:1.2;padding:2rem}.extension-manager-content{padding:2.5rem 2rem 2rem}.extension-manager-items{list-style:none;margin:0;text-align:center}.extension-manager-items .btn{border:1px solid #adadad;display:block;margin:1rem auto 0}.extension-manager-items .item-title{font-size:2.1rem;display:inline-block;text-align:left}.extension-manager-items .item-number{font-size:4.1rem;display:inline-block;line-height:.8;margin:0 5px 1.5rem 0;vertical-align:top}.extension-manager-items .item-date{font-size:2.6rem;margin-top:1px}.extension-manager-items .item-date-title{font-size:1.5rem}.extension-manager-items .item-install{margin:0 0 2rem}.sync-login-wrap{padding:0 10% 4rem}.sync-login-wrap .legend{font-size:2.6rem;color:#eb5202;float:left;font-weight:300;line-height:1.2;margin:-1rem 0 2.5rem;position:static;width:100%}.sync-login-wrap .legend._hidden{display:none}.sync-login-wrap .login-header{font-size:3.4rem;font-weight:300;margin:0 0 2rem}.sync-login-wrap .login-header span{display:inline-block;padding:.9rem 0 0;vertical-align:top}.sync-login-wrap h4{font-size:1.4rem;margin:0 0 2rem}.sync-login-wrap .sync-login-steps{margin:0 0 2rem 1.5rem}.sync-login-wrap .sync-login-steps li{padding:0 0 0 1rem}.sync-login-wrap .form-row .form-label{display:inline-block}.sync-login-wrap .form-row .form-label.required{padding-left:1.5rem}.sync-login-wrap .form-row .form-label.required:after{left:0;position:absolute;right:auto}.sync-login-wrap .form-row{max-width:28rem}.sync-login-wrap .form-actions{display:table;margin-top:-1.3rem}.sync-login-wrap .form-actions .links{display:table-header-group}.sync-login-wrap .form-actions .actions{padding:3rem 0 0}@media all and (max-width:1047px){.admin__menu .submenu li{min-width:19.8rem}.nav{padding-bottom:5.38rem;padding-left:1.5rem;text-align:center}.nav-bar{display:inline-block;float:none;margin-right:0;vertical-align:top}.nav .btn-group,.nav-bar-outer-actions{display:inline-block;float:none;margin-top:-8.48rem;text-align:center;vertical-align:top;width:100%}.nav-bar-outer-actions{padding-right:0}.nav-bar-outer-actions .outer-actions-inner-wrap{display:inline-block}.app-updater .nav{padding-bottom:1.7rem}.app-updater .nav-bar-outer-actions{margin-top:2rem}}@media all and (min-width:768px){.page-layout-admin-2columns-left .page-columns{margin-left:-30px}.page-layout-admin-2columns-left .page-columns:after{clear:both;content:'';display:table}.page-layout-admin-2columns-left .page-columns .main-col{width:calc((100%) * .75 - 30px);float:right}.page-layout-admin-2columns-left .page-columns .side-col{width:calc((100%) * .25 - 30px);float:left;margin-left:30px}.col-m-1,.col-m-10,.col-m-11,.col-m-12,.col-m-2,.col-m-3,.col-m-4,.col-m-5,.col-m-6,.col-m-7,.col-m-8,.col-m-9{float:left}.col-m-12{width:100%}.col-m-11{width:91.66666667%}.col-m-10{width:83.33333333%}.col-m-9{width:75%}.col-m-8{width:66.66666667%}.col-m-7{width:58.33333333%}.col-m-6{width:50%}.col-m-5{width:41.66666667%}.col-m-4{width:33.33333333%}.col-m-3{width:25%}.col-m-2{width:16.66666667%}.col-m-1{width:8.33333333%}.col-m-pull-12{right:100%}.col-m-pull-11{right:91.66666667%}.col-m-pull-10{right:83.33333333%}.col-m-pull-9{right:75%}.col-m-pull-8{right:66.66666667%}.col-m-pull-7{right:58.33333333%}.col-m-pull-6{right:50%}.col-m-pull-5{right:41.66666667%}.col-m-pull-4{right:33.33333333%}.col-m-pull-3{right:25%}.col-m-pull-2{right:16.66666667%}.col-m-pull-1{right:8.33333333%}.col-m-pull-0{right:auto}.col-m-push-12{left:100%}.col-m-push-11{left:91.66666667%}.col-m-push-10{left:83.33333333%}.col-m-push-9{left:75%}.col-m-push-8{left:66.66666667%}.col-m-push-7{left:58.33333333%}.col-m-push-6{left:50%}.col-m-push-5{left:41.66666667%}.col-m-push-4{left:33.33333333%}.col-m-push-3{left:25%}.col-m-push-2{left:16.66666667%}.col-m-push-1{left:8.33333333%}.col-m-push-0{left:auto}.col-m-offset-12{margin-left:100%}.col-m-offset-11{margin-left:91.66666667%}.col-m-offset-10{margin-left:83.33333333%}.col-m-offset-9{margin-left:75%}.col-m-offset-8{margin-left:66.66666667%}.col-m-offset-7{margin-left:58.33333333%}.col-m-offset-6{margin-left:50%}.col-m-offset-5{margin-left:41.66666667%}.col-m-offset-4{margin-left:33.33333333%}.col-m-offset-3{margin-left:25%}.col-m-offset-2{margin-left:16.66666667%}.col-m-offset-1{margin-left:8.33333333%}.col-m-offset-0{margin-left:0}.page-columns{margin-left:-30px}.page-columns:after{clear:both;content:'';display:table}.page-columns .page-inner-content{width:calc((100%) * .75 - 30px);float:right}.page-columns .page-inner-sidebar{width:calc((100%) * .25 - 30px);float:left;margin-left:30px}}@media all and (min-width:1048px){.col-l-1,.col-l-10,.col-l-11,.col-l-12,.col-l-2,.col-l-3,.col-l-4,.col-l-5,.col-l-6,.col-l-7,.col-l-8,.col-l-9{float:left}.col-l-12{width:100%}.col-l-11{width:91.66666667%}.col-l-10{width:83.33333333%}.col-l-9{width:75%}.col-l-8{width:66.66666667%}.col-l-7{width:58.33333333%}.col-l-6{width:50%}.col-l-5{width:41.66666667%}.col-l-4{width:33.33333333%}.col-l-3{width:25%}.col-l-2{width:16.66666667%}.col-l-1{width:8.33333333%}.col-l-pull-12{right:100%}.col-l-pull-11{right:91.66666667%}.col-l-pull-10{right:83.33333333%}.col-l-pull-9{right:75%}.col-l-pull-8{right:66.66666667%}.col-l-pull-7{right:58.33333333%}.col-l-pull-6{right:50%}.col-l-pull-5{right:41.66666667%}.col-l-pull-4{right:33.33333333%}.col-l-pull-3{right:25%}.col-l-pull-2{right:16.66666667%}.col-l-pull-1{right:8.33333333%}.col-l-pull-0{right:auto}.col-l-push-12{left:100%}.col-l-push-11{left:91.66666667%}.col-l-push-10{left:83.33333333%}.col-l-push-9{left:75%}.col-l-push-8{left:66.66666667%}.col-l-push-7{left:58.33333333%}.col-l-push-6{left:50%}.col-l-push-5{left:41.66666667%}.col-l-push-4{left:33.33333333%}.col-l-push-3{left:25%}.col-l-push-2{left:16.66666667%}.col-l-push-1{left:8.33333333%}.col-l-push-0{left:auto}.col-l-offset-12{margin-left:100%}.col-l-offset-11{margin-left:91.66666667%}.col-l-offset-10{margin-left:83.33333333%}.col-l-offset-9{margin-left:75%}.col-l-offset-8{margin-left:66.66666667%}.col-l-offset-7{margin-left:58.33333333%}.col-l-offset-6{margin-left:50%}.col-l-offset-5{margin-left:41.66666667%}.col-l-offset-4{margin-left:33.33333333%}.col-l-offset-3{margin-left:25%}.col-l-offset-2{margin-left:16.66666667%}.col-l-offset-1{margin-left:8.33333333%}.col-l-offset-0{margin-left:0}}@media all and (min-width:1440px){.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9{float:left}.col-xl-12{width:100%}.col-xl-11{width:91.66666667%}.col-xl-10{width:83.33333333%}.col-xl-9{width:75%}.col-xl-8{width:66.66666667%}.col-xl-7{width:58.33333333%}.col-xl-6{width:50%}.col-xl-5{width:41.66666667%}.col-xl-4{width:33.33333333%}.col-xl-3{width:25%}.col-xl-2{width:16.66666667%}.col-xl-1{width:8.33333333%}.col-xl-pull-12{right:100%}.col-xl-pull-11{right:91.66666667%}.col-xl-pull-10{right:83.33333333%}.col-xl-pull-9{right:75%}.col-xl-pull-8{right:66.66666667%}.col-xl-pull-7{right:58.33333333%}.col-xl-pull-6{right:50%}.col-xl-pull-5{right:41.66666667%}.col-xl-pull-4{right:33.33333333%}.col-xl-pull-3{right:25%}.col-xl-pull-2{right:16.66666667%}.col-xl-pull-1{right:8.33333333%}.col-xl-pull-0{right:auto}.col-xl-push-12{left:100%}.col-xl-push-11{left:91.66666667%}.col-xl-push-10{left:83.33333333%}.col-xl-push-9{left:75%}.col-xl-push-8{left:66.66666667%}.col-xl-push-7{left:58.33333333%}.col-xl-push-6{left:50%}.col-xl-push-5{left:41.66666667%}.col-xl-push-4{left:33.33333333%}.col-xl-push-3{left:25%}.col-xl-push-2{left:16.66666667%}.col-xl-push-1{left:8.33333333%}.col-xl-push-0{left:auto}.col-xl-offset-12{margin-left:100%}.col-xl-offset-11{margin-left:91.66666667%}.col-xl-offset-10{margin-left:83.33333333%}.col-xl-offset-9{margin-left:75%}.col-xl-offset-8{margin-left:66.66666667%}.col-xl-offset-7{margin-left:58.33333333%}.col-xl-offset-6{margin-left:50%}.col-xl-offset-5{margin-left:41.66666667%}.col-xl-offset-4{margin-left:33.33333333%}.col-xl-offset-3{margin-left:25%}.col-xl-offset-2{margin-left:16.66666667%}.col-xl-offset-1{margin-left:8.33333333%}.col-xl-offset-0{margin-left:0}}@media all and (max-width:767px){.abs-clearer-mobile:after,.nav-bar:after{clear:both;content:'';display:table}.list-definition>dt{float:none}.list-definition>dd{margin-left:0}.form-row .form-label{text-align:left}.form-row .form-label.required:after{position:static}.nav{padding-bottom:0;padding-left:0;padding-right:0}.nav-bar-outer-actions{margin-top:0}.nav-bar{display:block;margin-bottom:0;margin-left:auto;margin-right:auto;width:30.9rem}.nav-bar:before{display:none}.nav-bar>li{float:left;min-height:9rem}.nav-bar>li:after{display:none}.nav-bar>li:nth-child(4n){clear:both}.nav-bar a{line-height:1.4}.tooltip{display:none!important}.readiness-check-content{margin-right:2rem}.readiness-check-side{padding:2rem 0;position:static}.form-el-insider,.form-el-insider-wrap,.page-web-configuration .form-el-insider-input,.page-web-configuration .form-el-insider-input .form-el-input{display:block;width:100%}}@media all and (max-width:479px){.nav-bar{width:23.175rem}.nav-bar>li{width:7.725rem}.nav .btn-group .btn-wrap-try-again,.nav-bar-outer-actions .btn-wrap-try-again{clear:both;display:block;float:none;margin-left:auto;margin-right:auto;margin-top:1rem;padding-top:1rem}} diff --git a/setup/src/Magento/Setup/Console/Command/AbstractDependenciesCommand.php b/setup/src/Magento/Setup/Console/Command/AbstractDependenciesCommand.php index 0148152b0eaa6..fa4c9c897e7b1 100644 --- a/setup/src/Magento/Setup/Console/Command/AbstractDependenciesCommand.php +++ b/setup/src/Magento/Setup/Console/Command/AbstractDependenciesCommand.php @@ -1,6 +1,6 @@ objectManagerFactory = $objectManagerFactory; $this->validator = $validator; - $this->objectManager = $objectManager; + $this->objectManager = $objectManagerProvider->get(); + parent::__construct(); } @@ -373,6 +366,7 @@ private function getDeployableEntities($entities, $includedEntities, $excludedEn /** * {@inheritdoc} * @throws \InvalidArgumentException + * @throws LocalizedException */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -394,9 +388,9 @@ protected function execute(InputInterface $input, OutputInterface $output) list ($deployableLanguages, $deployableAreaThemeMap, $requestedThemes) = $this->prepareDeployableEntities($filesUtil); - $output->writeln("Requested languages: " . implode(', ', $deployableLanguages)); - $output->writeln("Requested areas: " . implode(', ', array_keys($deployableAreaThemeMap))); - $output->writeln("Requested themes: " . implode(', ', $requestedThemes)); + $output->writeln('Requested languages: ' . implode(', ', $deployableLanguages)); + $output->writeln('Requested areas: ' . implode(', ', array_keys($deployableAreaThemeMap))); + $output->writeln('Requested themes: ' . implode(', ', $requestedThemes)); /** @var $deployManager DeployManager */ $deployManager = $this->objectManager->create( @@ -415,11 +409,13 @@ protected function execute(InputInterface $input, OutputInterface $output) } } + $this->mockCache(); return $deployManager->deploy(); } /** * @param Files $filesUtil + * @throws \InvalidArgumentException * @return array * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -476,4 +472,18 @@ private function prepareDeployableEntities($filesUtil) return [$deployableLanguages, $deployableAreaThemeMap, $requestedThemes]; } + + /** + * Mock Cache class with dummy implementation + * + * @return void + */ + private function mockCache() + { + $this->objectManager->configure([ + 'preferences' => [ + Cache::class => DummyCache::class + ] + ]); + } } diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php index 3f577d9b31187..bc443379a6bd4 100644 --- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php +++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php @@ -1,6 +1,6 @@ componentRegistrar->getPaths(ComponentRegistrar::MODULE); $libraryPaths = $this->componentRegistrar->getPaths(ComponentRegistrar::LIBRARY); - $generationPath = $this->directoryList->getPath(DirectoryList::GENERATION); + $generationPath = $this->directoryList->getPath(DirectoryList::GENERATED_CODE); $this->objectManager->get(\Magento\Framework\App\Cache::class)->clean(); $compiledPathsList = [ @@ -152,7 +152,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->cleanupFilesystem( [ DirectoryList::CACHE, - DirectoryList::DI, + DirectoryList::GENERATED_METADATA, ] ); foreach ($operations as $operationCode => $arguments) { diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileMultiTenantCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileMultiTenantCommand.php deleted file mode 100644 index ac6a43189d7ab..0000000000000 --- a/setup/src/Magento/Setup/Console/Command/DiCompileMultiTenantCommand.php +++ /dev/null @@ -1,493 +0,0 @@ -objectManager = $objectManagerProvider->get(); - $this->directoryList = $directoryList; - $this->componentRegistrar = $componentRegistrar; - parent::__construct(); - } - - /** - * {@inheritdoc} - */ - protected function configure() - { - $options = [ - new InputOption( - self::INPUT_KEY_SERIALIZER, - null, - InputOption::VALUE_REQUIRED, - 'Serializer function that should be used (' . self::SERIALIZER_VALUE_SERIALIZE . '|' - . self::SERIALIZER_VALUE_IGBINARY . ') default: ' . self::SERIALIZER_VALUE_SERIALIZE - ), - new InputOption( - self::INPUT_KEY_EXTRA_CLASSES_FILE, - null, - InputOption::VALUE_REQUIRED, - 'Path to file with extra proxies and factories to generate' - ), - new InputOption( - self::INPUT_KEY_GENERATION, - null, - InputOption::VALUE_REQUIRED, - 'Absolute path to generated classes, /var/generation by default' - ), - new InputOption( - self::INPUT_KEY_DI, - null, - InputOption::VALUE_REQUIRED, - 'Absolute path to DI definitions directory, /var/di by default' - ), - new InputOption( - self::INPUT_KEY_EXCLUDE_PATTERN, - null, - InputOption::VALUE_REQUIRED, - 'Allows to exclude Paths from compilation (default is #[\\\\/]m1[\\\\/]#i)' - ), - ]; - $this->setName(self::NAME) - ->setDescription( - 'Generates all non-existing proxies and factories, and pre-compile class definitions, ' - . 'inheritance information and plugin definitions' - ) - ->setDefinition($options); - parent::configure(); - } - - /** - * Get module directories exclude patterns - * - * @return array - */ - private function getModuleExcludePatterns() - { - $modulesExcludePatterns = []; - foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $modulePath) { - $modulesExcludePatterns[] = "#^" . $modulePath . "/Test#"; - } - return $modulesExcludePatterns; - } - - /** - * Get library directories exclude patterns - * - * @return array - */ - private function getLibraryExcludePatterns() - { - $libraryExcludePatterns = []; - foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::LIBRARY) as $libraryPath) { - $libraryExcludePatterns[] = "#^" . $libraryPath . "/([\\w]+/)?Test#"; - } - return $libraryExcludePatterns; - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $errors = $this->validate($input); - if ($errors) { - $output->writeln($errors); - return; - } - - $generationDir = $input->getOption(self::INPUT_KEY_GENERATION) ? $input->getOption(self::INPUT_KEY_GENERATION) - : $this->directoryList->getPath(DirectoryList::GENERATION); - $modulesExcludePatterns = $this->getModuleExcludePatterns(); - $testExcludePatterns = [ - "#^" . $this->directoryList->getPath(DirectoryList::SETUP) . "/[\\w]+/[\\w]+/Test#", - "#^" . $this->directoryList->getRoot() . "/dev/tools/Magento/Tools/[\\w]+/Test#" - ]; - $librariesExcludePatterns = $this->getLibraryExcludePatterns(); - $testExcludePatterns = array_merge($testExcludePatterns, $modulesExcludePatterns, $librariesExcludePatterns); - $fileExcludePatterns = $input->getOption('exclude-pattern') ? - [$input->getOption(self::INPUT_KEY_EXCLUDE_PATTERN)] : ['#[\\\\/]M1[\\\\/]#i']; - $fileExcludePatterns = array_merge($fileExcludePatterns, $testExcludePatterns); - /** @var Writer\Console logWriter Writer model for success messages */ - $logWriter = new Writer\Console($output); - $this->log = new Log($logWriter, $logWriter); - AutoloaderRegistry::getAutoloader()->addPsr4('Magento\\', $generationDir . '/Magento/'); - // 1 Code generation - $this->generateCode($generationDir, $fileExcludePatterns, $input); - // 2. Compilation - $this->compileCode($generationDir, $fileExcludePatterns, $input); - //Reporter - $this->log->report(); - if (!$this->log->hasError()) { - $output->writeln( - 'On *nix systems, verify the Magento application has permissions to modify files ' - . 'created by the compiler in the "var" directory. For instance, if you run the Magento application ' - . 'using Apache, the owner of the files in the "var" directory should be the Apache user (example ' - . 'command: "chown -R www-data:www-data /var" where MAGENTO_ROOT is the Magento ' - . 'root directory).' - ); - } - } - - /** - * Generate Code - * - * @param string $generationDir - * @param array $fileExcludePatterns - * @param InputInterface $input - * @return void - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - public function generateCode($generationDir, $fileExcludePatterns, $input) - { - // 1.1 Code scan - $filePatterns = ['php' => '/.*\.php$/', 'di' => '/\/etc\/([a-zA-Z_]*\/di|di)\.xml$/']; - $directoryScanner = new Scanner\DirectoryScanner(); - foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $codeScanDir) { - $this->files = array_merge_recursive( - $this->files, - $directoryScanner->scan($codeScanDir, $filePatterns, $fileExcludePatterns) - ); - } - $this->files['di'][] = $this->directoryList->getPath( - \Magento\Framework\App\Filesystem\DirectoryList::CONFIG - ) . '/di.xml'; - $this->files['additional'] = [$input->getOption(self::INPUT_KEY_EXTRA_CLASSES_FILE)]; - $repositoryScanner = new Scanner\RepositoryScanner(); - $repositories = $repositoryScanner->collectEntities($this->files['di']); - $scanner = new Scanner\CompositeScanner(); - $scanner->addChild(new Scanner\PhpScanner($this->log), 'php'); - $scanner->addChild(new Scanner\XmlScanner($this->log), 'di'); - $scanner->addChild(new Scanner\ArrayScanner(), 'additional'); - $this->entities = $scanner->collectEntities($this->files); - $interceptorScanner = new Scanner\XmlInterceptorScanner(); - $this->entities['interceptors'] = $interceptorScanner->collectEntities($this->files['di']); - // 1.2 Generation of Factory and Additional Classes - $generatorIo = $this->objectManager->create( - \Magento\Framework\Code\Generator\Io::class, - ['generationDirectory' => $generationDir] - ); - $this->generator = $this->objectManager->create( - \Magento\Framework\Code\Generator::class, - ['ioObject' => $generatorIo] - ); - /** Initialize object manager for code generation based on configs */ - $this->generator->setObjectManager($this->objectManager); - $generatorAutoloader = new \Magento\Framework\Code\Generator\Autoloader($this->generator); - spl_autoload_register([$generatorAutoloader, 'load']); - - foreach ($repositories as $entityName) { - switch ($this->generator->generateClass($entityName)) { - case CodeGenerator::GENERATION_SUCCESS: - $this->log->add(Log::GENERATION_SUCCESS, $entityName); - break; - case CodeGenerator::GENERATION_ERROR: - $this->log->add(Log::GENERATION_ERROR, $entityName); - break; - case CodeGenerator::GENERATION_SKIP: - default: - //no log - break; - } - } - foreach (['php', 'additional'] as $type) { - sort($this->entities[$type]); - foreach ($this->entities[$type] as $entityName) { - switch ($this->generator->generateClass($entityName)) { - case CodeGenerator::GENERATION_SUCCESS: - $this->log->add(Log::GENERATION_SUCCESS, $entityName); - break; - case CodeGenerator::GENERATION_ERROR: - $this->log->add(Log::GENERATION_ERROR, $entityName); - break; - case CodeGenerator::GENERATION_SKIP: - default: - //no log - break; - } - } - } - } - - /** - * Compile Code - * - * @param string $generationDir - * @param array $fileExcludePatterns - * @param InputInterface $input - * @return void - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - private function compileCode($generationDir, $fileExcludePatterns, $input) - { - $diDir = $input->getOption(self::INPUT_KEY_DI) ? $input->getOption(self::INPUT_KEY_DI) : - $this->directoryList->getPath(DirectoryList::DI); - $relationsFile = $diDir . '/relations.ser'; - $pluginDefFile = $diDir . '/plugins.ser'; - $compilationDirs = [ - $this->directoryList->getPath(DirectoryList::SETUP) . '/Magento/Setup/Module', - $this->directoryList->getRoot() . '/dev/tools/Magento/Tools', - ]; - $compilationDirs = array_merge( - $compilationDirs, - $this->componentRegistrar->getPaths(ComponentRegistrar::MODULE), - $this->componentRegistrar->getPaths(ComponentRegistrar::LIBRARY) - ); - $serializer = $input->getOption(self::INPUT_KEY_SERIALIZER) == Igbinary::NAME ? new Igbinary() : new Standard(); - // 2.1 Code scan - $validator = new \Magento\Framework\Code\Validator(); - $validator->add(new \Magento\Framework\Code\Validator\ConstructorIntegrity()); - $validator->add(new \Magento\Framework\Code\Validator\ContextAggregation()); - $classesScanner = new \Magento\Setup\Module\Di\Code\Reader\ClassesScanner(); - $classesScanner->addExcludePatterns($fileExcludePatterns); - $directoryInstancesNamesList = new \Magento\Setup\Module\Di\Code\Reader\Decorator\Directory( - $this->log, - new \Magento\Framework\Code\Reader\ClassReader(), - $classesScanner, - $validator, - $generationDir - ); - foreach ($compilationDirs as $path) { - if (is_readable($path)) { - $directoryInstancesNamesList->getList($path); - } - } - $inheritanceScanner = new Scanner\InheritanceInterceptorScanner( - new \Magento\Framework\ObjectManager\InterceptableValidator() - ); - $this->entities['interceptors'] = $inheritanceScanner->collectEntities( - get_declared_classes(), - $this->entities['interceptors'] - ); - // 2.1.1 Generation of Proxy and Interceptor Classes - foreach (['interceptors', 'di'] as $type) { - foreach ($this->entities[$type] as $entityName) { - switch ($this->generator->generateClass($entityName)) { - case CodeGenerator::GENERATION_SUCCESS: - $this->log->add(Log::GENERATION_SUCCESS, $entityName); - break; - case CodeGenerator::GENERATION_ERROR: - $this->log->add(Log::GENERATION_ERROR, $entityName); - break; - case CodeGenerator::GENERATION_SKIP: - default: - //no log - break; - } - } - } - //2.1.2 Compile relations for Proxy/Interceptor classes - $directoryInstancesNamesList->getList($generationDir); - $relations = $directoryInstancesNamesList->getRelations(); - // 2.2 Compression - $relationsFileDir = dirname($relationsFile); - if (!file_exists($relationsFileDir)) { - mkdir($relationsFileDir, 0777, true); - } - $relations = array_filter($relations); - file_put_contents($relationsFile, $serializer->serialize($relations)); - // 3. Plugin Definition Compilation - $pluginScanner = new Scanner\CompositeScanner(); - $pluginScanner->addChild(new Scanner\PluginScanner(), 'di'); - $pluginDefinitions = []; - $pluginList = $pluginScanner->collectEntities($this->files); - $pluginDefinitionList = new \Magento\Framework\Interception\Definition\Runtime(); - foreach ($pluginList as $type => $entityList) { - foreach ($entityList as $entity) { - $pluginDefinitions[ltrim($entity, '\\')] = $pluginDefinitionList->getMethodList($entity); - } - } - $outputContent = $serializer->serialize($pluginDefinitions); - $pluginDefFileDir = dirname($pluginDefFile); - if (!file_exists($pluginDefFileDir)) { - mkdir($pluginDefFileDir, 0777, true); - } - file_put_contents($pluginDefFile, $outputContent); - } - - /** - * Check if all option values provided by the user are valid - * - * @param InputInterface $input - * @return string[] - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - private function validate(InputInterface $input) - { - $errors = []; - $options = $input->getOptions(); - foreach ($options as $key => $value) { - if (!$value) { - continue; - } - switch ($key) { - case self::INPUT_KEY_SERIALIZER: - if (($value !== self::SERIALIZER_VALUE_SERIALIZE) && ($value !== self::SERIALIZER_VALUE_IGBINARY)) { - $errors[] = 'Invalid value for command option \'' . self::INPUT_KEY_SERIALIZER - . '\'. Possible values (' . self::SERIALIZER_VALUE_SERIALIZE . '|' - . self::SERIALIZER_VALUE_IGBINARY . ').'; - } - break; - case self::INPUT_KEY_EXTRA_CLASSES_FILE: - if (!file_exists($value)) { - $errors[] = 'Path does not exist for the value of command option \'' - . self::INPUT_KEY_EXTRA_CLASSES_FILE . '\'.'; - } - break; - case self::INPUT_KEY_GENERATION: - $errorMsg = $this->validateOutputPath($value, self::INPUT_KEY_GENERATION); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } - break; - case self::INPUT_KEY_DI: - $errorMsg = $this->validateOutputPath($value, self::INPUT_KEY_DI); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } - break; - case self::INPUT_KEY_EXCLUDE_PATTERN: - if (@preg_match($value, null) === false) { - $errors[] = 'Invalid pattern for command option \'' . self::INPUT_KEY_EXCLUDE_PATTERN - . '\'.'; - } - break; - } - } - return $errors; - } - - /** - * Validate output path based on type - * - * @param string $value - * @param string $type - * @return string - */ - private function validateOutputPath($value, $type) - { - $errorMsg = ''; - if (!file_exists($value)) { - $errorMsg = 'Path does not exist for the value of command option \'' . $type . '\'.'; - } - if (file_exists($value) && !is_writeable($value)) { - $errorMsg .= 'Non-writable directory is provided by the value of command option \'' - . $type . '\'.'; - - } - return $errorMsg; - } -} diff --git a/setup/src/Magento/Setup/Console/Command/GenerateFixturesCommand.php b/setup/src/Magento/Setup/Console/Command/GenerateFixturesCommand.php index d91a1633ef038..9bc28bb97a905 100644 --- a/setup/src/Magento/Setup/Console/Command/GenerateFixturesCommand.php +++ b/setup/src/Magento/Setup/Console/Command/GenerateFixturesCommand.php @@ -1,11 +1,13 @@ fixtureModel; + $fixtureModel->loadConfig($input->getArgument(self::PROFILE_ARGUMENT)); $fixtureModel->initObjectManager(); $fixtureModel->loadFixtures(); - $fixtureModel->loadConfig($input->getArgument(self::PROFILE_ARGUMENT)); $output->writeln('Generating profile with following params:'); - foreach ($fixtureModel->getParamLabels() as $configKey => $label) { - $output->writeln(' |- ' . $label . ': ' . $fixtureModel->getValue($configKey) . ''); + foreach ($fixtureModel->getFixtures() as $fixture) { + $fixture->printInfo($output); } /** @var $config \Magento\Indexer\Model\Config */ @@ -95,14 +97,16 @@ protected function execute(InputInterface $input, OutputInterface $output) } foreach ($fixtureModel->getFixtures() as $fixture) { - $output->write($fixture->getActionTitle() . '... '); + $output->write('' . $fixture->getActionTitle() . '... '); $startTime = microtime(true); - $fixture->execute(); + $fixture->execute($output); $endTime = microtime(true); $resultTime = $endTime - $startTime; - $output->writeln(' done in ' . gmdate('H:i:s', $resultTime)); + $output->writeln(' done in ' . gmdate('H:i:s', $resultTime) . ''); } + $this->clearChangelog(); + foreach ($indexerListIds as $indexerId) { /** @var $indexer \Magento\Indexer\Model\Indexer */ $indexer = $indexerRegistry->get($indexerId['indexer_id']); @@ -123,4 +127,25 @@ protected function execute(InputInterface $input, OutputInterface $output) return \Magento\Framework\Console\Cli::RETURN_FAILURE; } } + + /** + * Clear changelog after generation + * + * @return void + */ + private function clearChangelog() + { + $viewConfig = $this->fixtureModel->getObjectManager()->create(CollectionInterface::class); + + /* @var ResourceConnection $resource */ + $resource = $this->fixtureModel->getObjectManager()->get(ResourceConnection::class); + + foreach ($viewConfig as $view) { + /* @var \Magento\Framework\Mview\ViewInterface $view */ + $changeLogTableName = $resource->getTableName($view->getChangelog()->getName()); + if ($resource->getConnection()->isTableExists($changeLogTableName)) { + $resource->getConnection()->truncateTable($changeLogTableName); + } + } + } } diff --git a/setup/src/Magento/Setup/Console/Command/I18nCollectPhrasesCommand.php b/setup/src/Magento/Setup/Console/Command/I18nCollectPhrasesCommand.php index 4731f9c4c5de4..c7cd5d6d9b235 100644 --- a/setup/src/Magento/Setup/Console/Command/I18nCollectPhrasesCommand.php +++ b/setup/src/Magento/Setup/Console/Command/I18nCollectPhrasesCommand.php @@ -1,6 +1,6 @@ installerFactory = $installerFactory; $this->deploymentConfig = $deploymentConfig; $this->objectManager = $objectManagerProvider->get(); + $this->localeValidator = $localeValidator; + $this->timezoneValidator = $timezoneValidator; + $this->currencyValidator = $currencyValidator; + $this->urlValidator = $urlValidator; parent::__construct(); } @@ -173,6 +205,7 @@ public function getOptionsList() public function validate(InputInterface $input) { $errors = []; + $errorMsg = ''; $options = $input->getOptions(); foreach ($options as $key => $value) { if (!$value) { @@ -180,99 +213,69 @@ public function validate(InputInterface $input) } switch ($key) { case StoreConfigurationDataMapper::KEY_BASE_URL: - /** @var Validator $url */ if (strcmp($value, '{{base_url}}') == 0) { break; } - $url = $this->objectManager->get(\Magento\Framework\Url\Validator::class); - if (!$url->isValid($value)) { - $errorMsgs = $url->getMessages(); - $errors[] = '' . 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL - . '\': ' . $errorMsgs[Validator::INVALID_URL] .''; - } + $errorMsg = $this->validateUrl( + $value, + StoreConfigurationDataMapper::KEY_BASE_URL, + ['http', 'https'] + ); + break; case StoreConfigurationDataMapper::KEY_LANGUAGE: - /** @var Locale $lists */ - $lists = $this->objectManager->get(\Magento\Framework\Validator\Locale::class); - $errorMsg = $this->validateCodes($lists, $value, StoreConfigurationDataMapper::KEY_LANGUAGE); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } + $errorMsg = $this->validateCodes( + $this->localeValidator, + $value, + StoreConfigurationDataMapper::KEY_LANGUAGE + ); break; case StoreConfigurationDataMapper::KEY_TIMEZONE: - /** @var Timezone $lists */ - $lists = $this->objectManager->get(\Magento\Framework\Validator\Timezone::class); - $errorMsg = $this->validateCodes($lists, $value, StoreConfigurationDataMapper::KEY_TIMEZONE); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } + $errorMsg = $this->validateCodes( + $this->timezoneValidator, + $value, + StoreConfigurationDataMapper::KEY_TIMEZONE + ); break; case StoreConfigurationDataMapper::KEY_CURRENCY: - /** @var Currency $lists */ - $lists = $this->objectManager->get(\Magento\Framework\Validator\Currency::class); - $errorMsg = $this->validateCodes($lists, $value, StoreConfigurationDataMapper::KEY_CURRENCY); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } + $errorMsg = $this->validateCodes( + $this->currencyValidator, + $value, + StoreConfigurationDataMapper::KEY_CURRENCY + ); break; case StoreConfigurationDataMapper::KEY_USE_SEF_URL: $errorMsg = $this->validateBinaryValue($value, StoreConfigurationDataMapper::KEY_USE_SEF_URL); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; case StoreConfigurationDataMapper::KEY_IS_SECURE: $errorMsg = $this->validateBinaryValue($value, StoreConfigurationDataMapper::KEY_IS_SECURE); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; case StoreConfigurationDataMapper::KEY_BASE_URL_SECURE: - try { - /** @var Validator $url */ - $url = $this->objectManager->get(\Magento\Framework\Url\Validator::class); - $errorMsgs = ''; - if (!$url->isValid($value)) { - $errorMsgs = $url->getMessages(); - if (!empty($errorMsgs)) { - $errors[] = '' . 'Command option \'' - . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE - . '\': ' . $errorMsgs[Validator::INVALID_URL] .''; - } - } - if (empty($errorMsgs) && strpos($value, 'https:') === false) { - throw new LocalizedException(new \Magento\Framework\Phrase("Invalid secure URL.")); - } - } catch (LocalizedException $e) { - $errors[] = '' . 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE - . '\': ' . $e->getLogMessage() .''; - } + $errorMsg = $this->validateUrl( + $value, + StoreConfigurationDataMapper::KEY_BASE_URL_SECURE, + ['https'] + ); break; case StoreConfigurationDataMapper::KEY_IS_SECURE_ADMIN: $errorMsg = $this->validateBinaryValue($value, StoreConfigurationDataMapper::KEY_IS_SECURE_ADMIN); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; case StoreConfigurationDataMapper::KEY_ADMIN_USE_SECURITY_KEY: $errorMsg = $this->validateBinaryValue( $value, StoreConfigurationDataMapper::KEY_ADMIN_USE_SECURITY_KEY ); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; case StoreConfigurationDataMapper::KEY_JS_LOGGING: $errorMsg = $this->validateBinaryValue( $value, StoreConfigurationDataMapper::KEY_JS_LOGGING ); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; } + if ($errorMsg !== '') { + $errors[] = $errorMsg; + } } return $errors; } @@ -296,7 +299,7 @@ private function validateBinaryValue($value, $key) /** * Validate codes for languages, currencies or timezones * - * @param Locale|Timezone|Currency $lists + * @param LocaleValidator|TimezoneValidator|CurrencyValidator $lists * @param string $code * @param string $type * @return string @@ -310,4 +313,31 @@ private function validateCodes($lists, $code, $type) } return $errorMsg; } + + /** + * Validate URL + * + * @param string $url + * @param string $option + * @param array $allowedSchemes + * @return string + */ + private function validateUrl($url, $option, array $allowedSchemes) + { + $errorMsg = ''; + + if (!$this->urlValidator->isValid($url, $allowedSchemes)) { + $errorTemplate = 'Command option \'%s\': Invalid URL \'%s\'.' + . ' Domain Name should contain only letters, digits and hyphen.' + . ' And you should use only following schemes: \'%s\'.'; + $errorMsg = sprintf( + $errorTemplate, + $option, + $url, + implode(', ', $allowedSchemes) + ); + } + + return $errorMsg; + } } diff --git a/setup/src/Magento/Setup/Console/Command/MaintenanceAllowIpsCommand.php b/setup/src/Magento/Setup/Console/Command/MaintenanceAllowIpsCommand.php index 40823d5a2b162..d1aec5fa8eac2 100644 --- a/setup/src/Magento/Setup/Console/Command/MaintenanceAllowIpsCommand.php +++ b/setup/src/Magento/Setup/Console/Command/MaintenanceAllowIpsCommand.php @@ -1,6 +1,6 @@ installSchema(); $installer->installDataFixtures(); if (!$keepGenerated) { - $output->writeln('Please re-run Magento compile command'); + $output->writeln('Please re-run Magento compile command. Use the command "setup:di:compile"'); } return \Magento\Framework\Console\Cli::RETURN_SUCCESS; diff --git a/setup/src/Magento/Setup/Console/CommandList.php b/setup/src/Magento/Setup/Console/CommandList.php index 4bb5ac0181a79..d986160fa4f13 100644 --- a/setup/src/Magento/Setup/Console/CommandList.php +++ b/setup/src/Magento/Setup/Console/CommandList.php @@ -1,6 +1,6 @@ serviceManager = $serviceManager; - $this->input = $input; + $this->serviceManager = $serviceManager; + $this->input = $input; $this->filesystemDriver = $filesystemDriver; } /** - * Determine whether a CLI command is for compilation, and if so, clear the directory + * Determine whether a CLI command is for compilation, and if so, clear the directory. * - * @throws \Magento\Framework\Exception\FileSystemException + * @throws FileSystemException if generation directory is read-only * @return void */ public function handleCompilerEnvironment() @@ -60,8 +76,14 @@ public function handleCompilerEnvironment() ? $mageInitParams[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] : []; $directoryList = new DirectoryList(BP, $mageDirs); - $compileDirList[] = $directoryList->getPath(DirectoryList::GENERATION); - $compileDirList[] = $directoryList->getPath(DirectoryList::DI); + $compileDirList[] = $directoryList->getPath(DirectoryList::GENERATED_CODE); + $compileDirList[] = $directoryList->getPath(DirectoryList::GENERATED_METADATA); + + if (!$this->getGenerationDirectoryAccess()->check()) { + throw new FileSystemException( + new Phrase('Generation directory can not be written.') + ); + } foreach ($compileDirList as $compileDir) { if ($this->filesystemDriver->isExists($compileDir)) { @@ -69,4 +91,18 @@ public function handleCompilerEnvironment() } } } + + /** + * Retrieves generation directory access checker. + * + * @return GenerationDirectoryAccess the generation directory access checker + */ + private function getGenerationDirectoryAccess() + { + if (null === $this->generationDirectoryAccess) { + $this->generationDirectoryAccess = new GenerationDirectoryAccess($this->serviceManager); + } + + return $this->generationDirectoryAccess; + } } diff --git a/setup/src/Magento/Setup/Controller/AddDatabase.php b/setup/src/Magento/Setup/Controller/AddDatabase.php index 65fb1b684f35b..c3cab8a47b2c8 100644 --- a/setup/src/Magento/Setup/Controller/AddDatabase.php +++ b/setup/src/Magento/Setup/Controller/AddDatabase.php @@ -1,6 +1,6 @@ urlValidator = $urlValidator; + } + + /** + * Validate URL + * + * @return JsonModel + */ + public function indexAction() + { + $params = Json::decode($this->getRequest()->getContent(), Json::TYPE_ARRAY); + $result = ['successUrl' => false, 'successSecureUrl' => true]; + + $hasBaseUrl = isset($params['address']['actual_base_url']); + $hasSecureBaseUrl = isset($params['https']['text']); + $hasSecureAdminUrl = !empty($params['https']['admin']); + $hasSecureFrontUrl = !empty($params['https']['front']); + $schemes = ['http', 'https']; + + // Validating of Base URL + if ($hasBaseUrl && $this->urlValidator->isValid($params['address']['actual_base_url'], $schemes)) { + $result['successUrl'] = true; + } + + // Validating of Secure Base URL + if ($hasSecureAdminUrl || $hasSecureFrontUrl) { + if (!($hasSecureBaseUrl && $this->urlValidator->isValid($params['https']['text'], $schemes))) { + $result['successSecureUrl'] = false; + } + } + + return new JsonModel($result); + } +} diff --git a/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php b/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php index 6833c1fba0c11..01461dd499586 100644 --- a/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php +++ b/setup/src/Magento/Setup/Controller/ValidateAdminCredentials.php @@ -1,6 +1,6 @@ userFactory = $userFactory; + $this->roleFactory = $roleFactory; + $this->userCollectionFactory = $userCollectionFactory; + } + + /** + * {@inheritdoc} + */ + public function execute() + { + $adminUsersNumber = $this->fixtureModel->getValue('admin_users', 0); + $adminUsersStartIndex = $this->userCollectionFactory->create()->getSize(); + + if ($adminUsersStartIndex >= $adminUsersNumber) { + return; + } + + $defaultAdminUser = $this->userFactory->create()->loadByUsername('admin'); + $defaultAdminRole = $this->roleFactory->create()->load($defaultAdminUser->getAclRole()); + + for ($i = $adminUsersStartIndex; $i <= $adminUsersNumber; $i++) { + $adminUser = $this->userFactory->create(); + $adminUser + ->setEmail('admin' . $i . '@example.com') + ->setFirstName('Firstname') + ->setLastName('Lastname') + ->setUserName('admin' . $i) + ->setPassword('123123q') + ->setIsActive(1); + $adminUser->save(); + + $role = $this->roleFactory->create(); + $role + ->setUserId($adminUser->getId()) + ->setRoleName('admin') + ->setRoleType($defaultAdminRole->getRoleType()) + ->setUserType($defaultAdminRole->getUserType()) + ->setTreeLevel($defaultAdminRole->getTreeLevel()) + ->setSortOrder($defaultAdminRole->getSortOrder()) + ->setParentId(1); + $role->save(); + } + } + + /** + * {@inheritdoc} + */ + public function getActionTitle() + { + return 'Generating admin users'; + } + + /** + * {@inheritdoc} + */ + public function introduceParamLabels() + { + return [ + 'admin_users' => 'Admin Users' + ]; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/AttributeSet/AttributeSetFixture.php b/setup/src/Magento/Setup/Fixtures/AttributeSet/AttributeSetFixture.php new file mode 100644 index 0000000000000..623a1844dd5f8 --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/AttributeSet/AttributeSetFixture.php @@ -0,0 +1,171 @@ +attributeRepository = $attributeRepository; + $this->attributeManagement = $attributeManagement; + $this->attributeFactory = $attributeFactory; + $this->optionFactory = $optionFactory; + $this->attributeSetFactory = $attributeSetFactory; + $this->attributeGroupFactory = $attributeGroupFactory; + $this->attributeSetManagement = $attributeSetManagement; + $this->attributeGroupRepository = $attributeGroupRepository; + } + + /** + * Create Attribute Set based on raw data. + * + * @param array $attributeSetData + * @param int $sortOrder + * @return array + */ + public function createAttributeSet(array $attributeSetData, $sortOrder = 3) + { + /** @var \Magento\Eav\Api\Data\AttributeSetInterface $attributeSet */ + $attributeSet = $this->attributeSetFactory->create(); + $attributeSet->setAttributeSetName($attributeSetData['name']); + $attributeSet->setEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); + + try { + $attributeSet = $this->attributeSetManagement->create($attributeSet, 4); + } catch (\Exception $e) { + return $this->getFormattedAttributeSetData($attributeSetData); + } + $attributeSetId = $attributeSet->getAttributeSetId(); + + /** @var \Magento\Eav\Api\Data\AttributeGroupInterface $attributeGroup */ + $attributeGroup = $this->attributeGroupFactory->create(); + $attributeGroup->setAttributeGroupName($attributeSet->getAttributeSetName() . ' - Group'); + $attributeGroup->setAttributeSetId($attributeSetId); + $this->attributeGroupRepository->save($attributeGroup); + $attributeGroupId = $attributeGroup->getAttributeGroupId(); + + $attributesData = array_key_exists(0, $attributeSetData['attributes']['attribute']) + ? $attributeSetData['attributes']['attribute'] : [$attributeSetData['attributes']['attribute']]; + foreach ($attributesData as $attributeData) { + //Create Attribute + $optionsData = array_key_exists(0, $attributeData['options']['option']) + ? $attributeData['options']['option'] : [$attributeData['options']['option']]; + $options = []; + foreach ($optionsData as $optionData) { + $option = $this->optionFactory->create(['data' => $optionData]); + $options[] = $option; + } + + /** @var ProductAttributeInterface $attribute */ + $attribute = $this->attributeFactory->create(['data' => $attributeData]); + $attribute->setOptions($options); + $attribute->setNote('auto'); + + $productAttribute = $this->attributeRepository->save($attribute); + $attributeId = $productAttribute->getAttributeId(); + + //Associate Attribute to Attribute Set + $this->attributeManagement->assign($attributeSetId, $attributeGroupId, $attributeId, $sortOrder); + } + + return $this->getFormattedAttributeSetData($attributeSetData); + } + + /** + * Return formatted attribute set data + * + * @param array $attributeSetData + * @return array + */ + private function getFormattedAttributeSetData($attributeSetData) + { + $attributesData = array_key_exists(0, $attributeSetData['attributes']['attribute']) + ? $attributeSetData['attributes']['attribute'] : [$attributeSetData['attributes']['attribute']]; + $attributes = []; + foreach ($attributesData as $attributeData) { + $optionsData = array_key_exists(0, $attributeData['options']['option']) + ? $attributeData['options']['option'] : [$attributeData['options']['option']]; + $optionsData = array_map(function ($option) { + return $option['label']; + }, $optionsData); + $attributes[] = [ + 'name' => $attributeData['attribute_code'], + 'values' => $optionsData + ]; + } + + return [ + 'name' => $attributeSetData['name'], + 'attributes' => $attributes + ]; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php b/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php new file mode 100644 index 0000000000000..7274f545feb9e --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php @@ -0,0 +1,95 @@ + 1, + 'is_visible_on_front' => 1, + 'is_visible_in_advanced_search' => 0, + 'attribute_code' => 'attribute_', + 'backend_type' => '', + 'is_searchable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'frontend_label' => 'Attribute ', + 'frontend_input' => 'select', + ]; + + /** + * Generate Data for Fixture to create an Attribute Set with specified pattern. + * + * @param string $name + * @param int $attributesPerSet + * @param int $optionsPerAttribute + * @param callable $attributePattern callback in f($index, $attributeData) format + * @return array + */ + public function generateAttributeSet( + $name, + $attributesPerSet, + $optionsPerAttribute, + $attributePattern = null + ) { + $attributeSet = [ + 'name' => $name, + 'attributes' => [] + ]; + for ($index = 1; $index <= $attributesPerSet; $index++) { + $attributeData = $this->generateAttribute( + $index, + $optionsPerAttribute + ); + if (is_callable($attributePattern)) { + $attributeData = $attributePattern($index, $attributeData); + } + $attributeSet['attributes']['attribute'][] = $attributeData; + } + + return $attributeSet; + } + + /** + * Generate Attributes for Set. + * + * @param int $index + * @param int $optionsPerAttribute + * @return array + */ + private function generateAttribute($index, $optionsPerAttribute) + { + $attribute = $this->attributePattern; // copy pattern + $attribute['attribute_code'] = $attribute['attribute_code'] . $index; + $attribute['frontend_label'] = $attribute['frontend_label'] . $index; + $attribute['options'] = ['option' => $this->generateOptions($optionsPerAttribute)]; + $attribute['default_option'] = $attribute['options']['option'][0]['label']; + return $attribute; + } + + /** + * Generate Options for Attribute. + * + * @param int $optionsPerAttribute + * @return array + */ + private function generateOptions($optionsPerAttribute) + { + $options = []; + for ($index = 1; $index <= $optionsPerAttribute; $index++) { + $options[] = [ + 'label' => 'option ' . $index, + 'value' => 'option_' . $index + ]; + } + + return $options; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/AttributeSet/SwatchesGenerator.php b/setup/src/Magento/Setup/Fixtures/AttributeSet/SwatchesGenerator.php new file mode 100644 index 0000000000000..5210f5360027a --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/AttributeSet/SwatchesGenerator.php @@ -0,0 +1,162 @@ +filesystem = $filesystem; + $this->mediaConfig = $config; + $this->swatchHelper = $swatchHelper; + } + + /** + * Generates data for Swatch Attribute of the required type + * + * @param int $optionCount + * @param string $data + * @param string $type + * @return array + */ + public function generateSwatchData($optionCount, $data, $type) + { + if ($type === null) { + return []; + } + + $attribute['swatch_input_type'] = Swatch::SWATCH_INPUT_TYPE_VISUAL; + $attribute['swatchvisual']['value'] = array_reduce( + range(1, $optionCount), + function ($values, $index) use ($optionCount, $data, $type) { + if ($type === 'image') { + $values['option_' . $index] = $this->generateSwatchImage($data . $index); + } + if ($type === 'color') { + $values['option_' . $index] = $this->generateSwatchColor($index / $optionCount); + } + return $values; + }, + [] + ); + $attribute['optionvisual']['value'] = array_reduce( + range(1, $optionCount), + function ($values, $index) use ($optionCount) { + $values['option_' . $index] = ['option ' . $index]; + return $values; + }, + [] + ); + + return $attribute; + } + + /** + * Generate hex-coded color for Swatch Attribute based on provided index + * + * Colors will change gradually according to index value. + * + * @param int $index + * @return string + */ + private function generateSwatchColor($index) + { + return '#' . str_repeat(dechex(255 * $index), 3); + } + + /** + * Generate and save image for Swatch Attribute + * + * Image is generated with a set background color rgb(240, 240, 240), random foreground color, and pattern which + * is based on the binary representation of $data. + * + * @param string $data String value to be used for generation. + * @return string Path to the image file. + */ + private function generateSwatchImage($data) + { + $binaryData = ''; + $data = str_split(sha1($data), 2); + foreach ($data as $item) { + $binaryData .= base_convert($item, 16, 2); + } + $binaryData = str_split($binaryData, 1); + + $image = imagecreate(self::GENERATED_SWATCH_WIDTH, self::GENERATED_SWATCH_HEIGHT); + $bgColor = imagecolorallocate($image, 240, 240, 240); + $fgColor = imagecolorallocate($image, mt_rand(0, 230), mt_rand(0, 230), mt_rand(0, 230)); + $colors = [$fgColor, $bgColor]; + imagefilledrectangle($image, 0, 0, self::GENERATED_SWATCH_WIDTH, self::GENERATED_SWATCH_HEIGHT, $bgColor); + + for ($row = 10; $row < 100; $row += 18) { + for ($col = 0; $col < 90; $col += 18) { + next($binaryData); + imagefilledrectangle($image, $row, $col, $row + 18, $col + 18, $colors[current($binaryData)]); + } + } + + $mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $absolutePathToMedia = $mediaDirectory->getAbsolutePath($this->mediaConfig->getBaseTmpMediaPath()); + $relativePathToMedia = $mediaDirectory->getRelativePath($this->mediaConfig->getBaseTmpMediaPath()); + $mediaDirectory->create($relativePathToMedia); + + imagejpeg($image, $absolutePathToMedia . DIRECTORY_SEPARATOR . self::GENERATED_SWATCH_TMP_NAME, 100); + $imagePath = substr($this->swatchHelper->moveImageFromTmp(self::GENERATED_SWATCH_TMP_NAME), 1); + $this->swatchHelper->generateSwatchVariations($imagePath); + + return $imagePath; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/AttributeSetsFixture.php b/setup/src/Magento/Setup/Fixtures/AttributeSetsFixture.php new file mode 100644 index 0000000000000..a555381438075 --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/AttributeSetsFixture.php @@ -0,0 +1,99 @@ +attributeSetsFixture = $attributeSetsFixture; + $this->pattern = $pattern; + } + + /** + * {@inheritdoc} + */ + public function execute() + { + $attributeSets = $this->fixtureModel->getValue('attribute_sets', null); + if ($attributeSets !== null) { + foreach ($attributeSets['attribute_set'] as $attributeSetData) { + $this->attributeSetsFixture->createAttributeSet($attributeSetData); + } + } + + $attributeSetsCount = $this->fixtureModel->getValue('product_attribute_sets', null); + if ($attributeSetsCount !== null) { + for ($index = 1; $index <= $attributeSetsCount; $index++) { + $this->attributeSetsFixture->createAttributeSet( + $this->pattern->generateAttributeSet( + self::PRODUCT_SET_NAME . $index, + $this->fixtureModel->getValue('product_attribute_sets_attributes', 3), + $this->fixtureModel->getValue('product_attribute_sets_attributes_values', 3), + function ($attributeIndex, $attribute) use ($index) { + return array_replace_recursive( + $attribute, + [ + 'attribute_code' => "attribute_set{$index}_" . $attributeIndex, + ] + ); + } + ) + ); + } + } + } + + /** + * {@inheritdoc} + */ + public function getActionTitle() + { + return 'Generating attribute sets'; + } + + /** + * {@inheritdoc} + */ + public function introduceParamLabels() + { + return [ + 'attribute_sets' => 'Attribute Sets (Default)', + 'product_attribute_sets' => 'Attribute Sets (Extra)' + ]; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/BundleProductsFixture.php b/setup/src/Magento/Setup/Fixtures/BundleProductsFixture.php new file mode 100644 index 0000000000000..038026f097bf4 --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/BundleProductsFixture.php @@ -0,0 +1,261 @@ +{products amount} + * {bundle product options amount} + * {amount of simple products per each option} + * + * Products will be uniformly distributed per categories and websites + * If node "assign_entities_to_all_websites" from profile is set to "1" then products will be assigned to all websites + * + * @see setup/performance-toolkit/profiles/ce/small.xml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class BundleProductsFixture extends Fixture +{ + /** + * Bundle sku pattern with entity number and suffix. Suffix equals "{options}-{variations_per_option}" + */ + const SKU_PATTERN = 'Bundle Product %s - %s'; + + /** + * @var int + */ + protected $priority = 42; + + /** + * @var \Magento\Setup\Model\FixtureGenerator\ProductGenerator + */ + private $productGenerator; + + /** + * @var \Magento\Setup\Model\FixtureGenerator\BundleProductGenerator + */ + private $bundleProductGenerator; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory + */ + private $productCollectionFactory; + + /** + * @var int + */ + private $productStartIndex; + + /** + * @var ProductsAmountProvider + */ + private $productsAmountProvider; + + /** + * @var WebsiteCategoryProvider + */ + private $websiteCategoryProvider; + + /** + * @var PriceProvider + */ + private $priceProvider; + + /** + * @param FixtureModel $fixtureModel + * @param \Magento\Setup\Model\FixtureGenerator\ProductGenerator $productGenerator + * @param \Magento\Setup\Model\FixtureGenerator\BundleProductGenerator $bundleProductGenerator + * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory + * @param ProductsAmountProvider $productsAmountProvider + * @param WebsiteCategoryProvider $websiteCategoryProvider + * @param PriceProvider $priceProvider + */ + public function __construct( + FixtureModel $fixtureModel, + \Magento\Setup\Model\FixtureGenerator\ProductGenerator $productGenerator, + \Magento\Setup\Model\FixtureGenerator\BundleProductGenerator $bundleProductGenerator, + \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, + ProductsAmountProvider $productsAmountProvider, + WebsiteCategoryProvider $websiteCategoryProvider, + PriceProvider $priceProvider + ) { + parent::__construct($fixtureModel); + $this->productGenerator = $productGenerator; + $this->bundleProductGenerator = $bundleProductGenerator; + $this->productCollectionFactory = $productCollectionFactory; + $this->productsAmountProvider = $productsAmountProvider; + $this->websiteCategoryProvider = $websiteCategoryProvider; + $this->priceProvider = $priceProvider; + } + + /** + * {@inheritdoc} + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function execute() + { + $bundlesAmount = $this->fixtureModel->getValue('bundle_products', 0); + $bundleOptions = $this->fixtureModel->getValue('bundle_products_options', 1); + $bundleProductsPerOption = $this->fixtureModel->getValue('bundle_products_variation', 10); + $bundleOptionSuffix = $bundleOptions . '-' . $bundleProductsPerOption; + $variationCount = $bundleOptions * $bundleProductsPerOption; + $bundlesAmount = $this->productsAmountProvider->getAmount( + $bundlesAmount, + $this->getBundleSkuPattern($bundleOptionSuffix) + ); + + if (!$bundlesAmount) { + return; + } + $variationSkuClosure = function ($productId, $entityNumber) use ($bundleOptionSuffix, $variationCount) { + $productIndex = $this->getBundleProductIndex($entityNumber, $variationCount); + $variationIndex = $this->getBundleVariationIndex($entityNumber, $variationCount) ; + + return sprintf($this->getBundleOptionItemSkuPattern($bundleOptionSuffix), $productIndex, $variationIndex); + }; + $fixtureMap = [ + 'name' => $variationSkuClosure, + 'sku' => $variationSkuClosure, + 'price' => function ($index, $entityNumber) { + return $this->priceProvider->getPrice($entityNumber); + }, + 'website_ids' => function ($index, $entityNumber) use ($variationCount) { + $configurableIndex = $this->getBundleProductIndex($entityNumber, $variationCount); + + return $this->websiteCategoryProvider->getWebsiteIds($configurableIndex); + }, + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + ]; + $this->productGenerator->generate($bundlesAmount * $bundleOptions * $bundleProductsPerOption, $fixtureMap); + + $optionPriceType = [ + LinkInterface::PRICE_TYPE_FIXED, + LinkInterface::PRICE_TYPE_PERCENT, + ]; + $priceTypeClosure = function ($index) use ($optionPriceType) { + return $optionPriceType[$index % count($optionPriceType)]; + }; + $skuClosure = function ($index, $entityNumber) use ($bundleOptionSuffix) { + return sprintf( + $this->getBundleSkuPattern($bundleOptionSuffix), + $entityNumber + $this->getNewProductStartIndex() + ); + }; + $fixtureMap = [ + '_bundle_options' => $bundleOptions, + '_bundle_products_per_option' => $bundleProductsPerOption, + '_bundle_variation_sku_pattern' => sprintf( + $this->getBundleOptionItemSkuPattern($bundleOptionSuffix), + $this->getNewProductStartIndex(), + '%s' + ), + 'type_id' => Type::TYPE_CODE, + 'name' => $skuClosure, + 'sku' => $skuClosure, + 'meta_title' => $skuClosure, + 'price' => function ($index) use ($priceTypeClosure) { + return $priceTypeClosure($index) === LinkInterface::PRICE_TYPE_PERCENT + ? mt_rand(10, 90) + : $this->priceProvider->getPrice($index); + }, + 'priceType' => $priceTypeClosure, + 'website_ids' => function ($index, $entityNumber) { + return $this->websiteCategoryProvider->getWebsiteIds($entityNumber + $this->getNewProductStartIndex()); + }, + 'category_ids' => function ($index, $entityNumber) { + return $this->websiteCategoryProvider->getCategoryId($entityNumber + $this->getNewProductStartIndex()); + }, + ]; + $this->bundleProductGenerator->generate($bundlesAmount, $fixtureMap); + } + + /** + * Get sku pattern for bundle product option item + * + * @param string $bundleOptionSuffix + * @return string + */ + private function getBundleOptionItemSkuPattern($bundleOptionSuffix) + { + return $this->getBundleSkuPattern($bundleOptionSuffix) . ' - option %s'; + } + + /** + * Get sku pattern for bundle product. Replace suffix pattern with passed value + * + * @param string $bundleOptionSuffix + * @return string + */ + private function getBundleSkuPattern($bundleOptionSuffix) + { + return sprintf(self::SKU_PATTERN, '%s', $bundleOptionSuffix); + } + + /** + * Get start index for product number generation + * + * @return int + */ + private function getNewProductStartIndex() + { + if (null === $this->productStartIndex) { + $this->productStartIndex = $this->productCollectionFactory->create() + ->addFieldToFilter('type_id', Type::TYPE_CODE) + ->getSize() + 1; + } + + return $this->productStartIndex; + } + + /** + * Get bundle product index number + * + * @param int $entityNumber + * @param int $variationCount + * @return float + */ + private function getBundleProductIndex($entityNumber, $variationCount) + { + return floor($entityNumber / $variationCount) + $this->getNewProductStartIndex(); + } + + /** + * Get bundle variation index number + * + * @param int $entityNumber + * @param int $variationCount + * @return float + */ + private function getBundleVariationIndex($entityNumber, $variationCount) + { + return $entityNumber % $variationCount + 1; + } + + /** + * {@inheritdoc} + */ + public function getActionTitle() + { + return 'Generating bundle products'; + } + + /** + * {@inheritdoc} + */ + public function introduceParamLabels() + { + return [ + 'bundle_products' => 'Bundle products', + ]; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/CartPriceRulesFixture.php b/setup/src/Magento/Setup/Fixtures/CartPriceRulesFixture.php index 5e6a36d2f1d03..7647a6776e2a7 100755 --- a/setup/src/Magento/Setup/Fixtures/CartPriceRulesFixture.php +++ b/setup/src/Magento/Setup/Fixtures/CartPriceRulesFixture.php @@ -1,6 +1,6 @@ {amount of categories} + * {Nesting level of categories} + * + * If config "assign_entities_to_all_websites" set to "0" then all categories will be + * uniformly distributed per root categories, else all categories assigned to one root category */ class CategoriesFixture extends Fixture { + /** + * @var StoreManager + */ + private $storeManager; + + /** + * @var CategoryFactory + */ + private $categoryFactory; + + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * @var int + */ + private $firstLevelCategoryIndex; + + /** + * @var array + */ + private $rootCategoriesIds; + + /** + * @var int + */ + private $categoriesNumber; + + /** + * @var int + */ + private $maxNestingLevel; + + /** + * CategoriesFixture constructor. + * @param FixtureModel $fixtureModel + * @param StoreManager $storeManager + * @param CategoryFactory $categoryFactory + * @param CollectionFactory $collectionFactory + */ + public function __construct( + FixtureModel $fixtureModel, + StoreManager $storeManager, + CategoryFactory $categoryFactory, + CollectionFactory $collectionFactory + ) { + parent::__construct($fixtureModel); + $this->storeManager = $storeManager; + $this->categoryFactory = $categoryFactory; + $this->collectionFactory = $collectionFactory; + } + /** * @var int */ @@ -21,61 +86,154 @@ class CategoriesFixture extends Fixture */ public function execute() { - $categoriesNumber = $this->fixtureModel->getValue('categories', 0); - if (!$categoriesNumber) { + $this->categoriesNumber = $this->getCategoriesAmount(); + if (!$this->categoriesNumber) { return; } - $maxNestingLevel = $this->fixtureModel->getValue('categories_nesting_level', 3); - $this->fixtureModel->resetObjectManager(); - - /** @var \Magento\Store\Model\StoreManager $storeManager */ - $storeManager = $this->fixtureModel->getObjectManager()->create(\Magento\Store\Model\StoreManager::class); - /** @var $category \Magento\Catalog\Model\Category */ - $category = $this->fixtureModel->getObjectManager()->create(\Magento\Catalog\Model\Category::class); - - $storeGroups = $storeManager->getGroups(); - $i = 0; - foreach ($storeGroups as $storeGroup) { - $parentCategoryId[$i] = $defaultParentCategoryId[$i] = $storeGroup->getRootCategoryId(); - $nestingLevel[$i] = 1; - $nestingPath[$i] = "1/$parentCategoryId[$i]"; - $categoryPath[$i] = ''; - $i++; + $this->maxNestingLevel = $this->fixtureModel->getValue('categories_nesting_level', 3); + + $categoriesNumberOnLevel = abs(ceil(pow($this->categoriesNumber, 1 / $this->maxNestingLevel) - 2)); + foreach ($this->getRootCategoriesIds() as $parentCategoryId) { + $category = $this->categoryFactory->create(); + $category->load($parentCategoryId); + // Need for generation url rewrites per all category store view + $category->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $categoryIndex = 1; + $this->generateCategories( + $category, + $categoriesNumberOnLevel, + 1, + $categoryIndex + ); } - $groupNumber = 0; - $categoryIndex = 1; - - while ($categoryIndex <= $categoriesNumber) { - $category->setId(null) - ->setUrlKey(null) - ->setUrlPath(null) - ->setName("Category $categoryIndex") - ->setParentId($parentCategoryId[$groupNumber]) - ->setPath($nestingPath[$groupNumber]) - ->setLevel($nestingLevel[$groupNumber] + 1) - ->setAvailableSortBy('name') - ->setIsAnchor(false) - ->setDefaultSortBy('name') - ->setIsActive(true) - ->save(); - $categoryIndex++; - $categoryPath[$groupNumber] .= '/' . $category->getName(); - - if ($nestingLevel[$groupNumber]++ == $maxNestingLevel) { - $nestingLevel[$groupNumber] = 1; - $parentCategoryId[$groupNumber] = $defaultParentCategoryId[$groupNumber]; - $nestingPath[$groupNumber] = '1'; - $categoryPath[$groupNumber] = ''; - } else { - $parentCategoryId[$groupNumber] = $category->getId(); + } + + /** + * Generate categories + * + * @param Category $parentCategory + * @param int $categoriesNumberOnLevel + * @param int $nestingLevel + * @param int $categoryIndex + * @return void + */ + private function generateCategories( + Category $parentCategory, + $categoriesNumberOnLevel, + $nestingLevel, + &$categoryIndex + ) { + $maxCategoriesNumberOnLevel = $nestingLevel === 1 ? $this->categoriesNumber : $categoriesNumberOnLevel; + for ($i = 0; $i < $maxCategoriesNumberOnLevel && $categoryIndex <= $this->categoriesNumber; $i++) { + try { + $category = clone $parentCategory; + $category->setId(null) + ->setUrlKey(null) + ->setUrlPath(null) + ->setName($this->getCategoryName($parentCategory, $nestingLevel, $i)) + ->setParentId($parentCategory->getId()) + ->setLevel($parentCategory->getLevel() + 1) + ->setAvailableSortBy('name') + ->setIsAnchor($nestingLevel <= 2) + ->setDefaultSortBy('name') + ->setIsActive(true); + $category->save(); + $categoryIndex++; + if ($nestingLevel < $this->maxNestingLevel) { + $this->generateCategories( + $category, + $categoriesNumberOnLevel, + $nestingLevel + 1, + $categoryIndex + ); + } + } catch (\Magento\Framework\Exception\AlreadyExistsException $e) { + $categoryIndex++; + continue; + } catch (\Magento\Framework\DB\Adapter\DuplicateException $e) { + $categoryIndex++; + continue; } - $nestingPath[$groupNumber] .= "/$parentCategoryId[$groupNumber]"; + } + } + + /** + * Get category name based on parent category and current level + * + * @param Category $parentCategory + * @param int $nestingLevel + * @param int $index + * @return string + */ + private function getCategoryName($parentCategory, $nestingLevel, $index) + { + $categoryNameSuffix = $nestingLevel === 1 ? $this->getFirstLevelCategoryIndex() + $index : $index + 1; + return ($nestingLevel === 1 ? $this->getCategoryPrefix() . ' ' : $parentCategory->getName() . '.') + . $categoryNameSuffix; + } - $groupNumber++; - if ($groupNumber == count($defaultParentCategoryId)) { - $groupNumber = 0; + /** + * Get ids of root categories + * + * @return int[] + */ + private function getRootCategoriesIds() + { + if (null === $this->rootCategoriesIds) { + $this->rootCategoriesIds = []; + foreach ($this->storeManager->getGroups() as $storeGroup) { + $this->rootCategoriesIds[] = $storeGroup->getRootCategoryId(); + // in this case root category will be the same for all store groups + if ((bool)$this->fixtureModel->getValue('assign_entities_to_all_websites', false)) { + break; + } } } + + return $this->rootCategoriesIds; + } + + /** + * Get categories amount for generation + * + * @return int + */ + private function getCategoriesAmount() + { + $categoriesAmount = $this->collectionFactory->create()->getSize(); + $rootCategories = count($this->getRootCategoriesIds()); + $categoriesNumber = $this->fixtureModel->getValue('categories', 0) - ($categoriesAmount - $rootCategories - 1); + + return max( + 0, + ceil($categoriesNumber / $rootCategories) + ); + } + + /** + * Get next category index, which will be used as index of first-level category + * + * @return int + */ + private function getFirstLevelCategoryIndex() + { + if (null === $this->firstLevelCategoryIndex) { + $this->firstLevelCategoryIndex = $this->collectionFactory->create() + ->addFieldToFilter('level', 2) + ->getSize() + 1; + } + + return $this->firstLevelCategoryIndex; + } + + /** + * Get Category name prefix + * + * @return string + */ + private function getCategoryPrefix() + { + return 'Category'; } /** diff --git a/setup/src/Magento/Setup/Fixtures/CategoryResolver.php b/setup/src/Magento/Setup/Fixtures/CategoryResolver.php new file mode 100644 index 0000000000000..037aef68dfb5b --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/CategoryResolver.php @@ -0,0 +1,104 @@ +storeManager = $storeManager; + $this->categoryFactory = $categoryFactory; + $this->collectionFactory = $collectionFactory; + $this->categoryRepository = $categoryRepository; + } + + /** + * Get category id + * + * @param int $websiteId + * @param string $categoryName + * @return int + */ + public function getCategory($websiteId, $categoryName) + { + $categoryKey = $websiteId . $categoryName; + + if (!isset($this->categories[$categoryKey])) { + $website = $this->storeManager->getWebsite($websiteId); + $rootCategoryId = $website->getDefaultGroup()->getRootCategoryId(); + $website->getDefaultGroup()->getStoreId(); + $category = $this->collectionFactory->create() + ->addFieldToFilter('parent_id', $rootCategoryId) + ->addFieldToFilter('name', $categoryName) + ->fetchItem(); + if ($category && $category->getId()) { + $this->categories[$categoryKey] = $category->getId(); + } else { + $category = $this->categoryFactory->create( + [ + 'data' => [ + 'parent_id' => $rootCategoryId, + 'name' => $categoryName, + 'position' => 1, + 'is_active' => true, + 'available_sort_by' => ['position', 'name'], + 'url_key' => $categoryName . '-' . $websiteId + ] + ] + ); + $category = $this->categoryRepository->save($category); + $this->categories[$categoryKey] = $category->getId(); + } + } + + return $this->categories[$categoryKey]; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/ConfigsApplyFixture.php b/setup/src/Magento/Setup/Fixtures/ConfigsApplyFixture.php index bc8362890b638..df86ec12164eb 100644 --- a/setup/src/Magento/Setup/Fixtures/ConfigsApplyFixture.php +++ b/setup/src/Magento/Setup/Fixtures/ConfigsApplyFixture.php @@ -1,6 +1,6 @@ {products amount} + * + * 2.1 Generate products based on existing attribute set: + * + * + * {Existing attribute set name} + * {Configurable sku pattern with %s} + * {Amount of configurable products} + * [{Category Name}] By default category name from CategoriesFixture will be used + * color|image Type of Swatch attribute + * + * + * + * 2.2 Generate products based on dynamically created attribute set with specified amount of attributes and options + * + * + * {Amount of attributes in configurable product} + * {Amount of options per attribute} + * {Configurable sku pattern with %s} + * {Amount of configurable products} + * [{Category Name}] By default category name from CategoriesFixture will be used + * color|image Type of Swatch attribute + * + * + * + * Products will be uniformly distributed per categories and websites + * If node "assign_entities_to_all_websites" from profile is set to "1" then products will be assigned to all websites + * + * @see setup/performance-toolkit/profiles/ce/small.xml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ConfigurableProductsFixture extends Fixture { @@ -19,498 +60,785 @@ class ConfigurableProductsFixture extends Fixture */ protected $priority = 50; - //@codingStandardsIgnoreStart /** - * Get CSV template headers + * @var array + */ + private $searchConfig; + + /** + * @var DataGenerator + */ + private $dataGenerator; + + /** + * @var AttributeSet\AttributeSetFixture + */ + private $attributeSetsFixture; + + /** + * @var AttributeSet\Pattern + */ + private $attributePattern; + + /** + * @var ProductGenerator + */ + private $productGenerator; + + /** + * @var CollectionFactory + */ + private $attributeCollectionFactory; + + /** + * @var ConfigurableProductGenerator + */ + private $configurableProductGenerator; + + /** + * @var ProductCollectionFactory + */ + private $productCollectionFactory; + + /** + * @var int + */ + private $productStartIndex; + + /** + * @var ProductsAmountProvider + */ + private $productsAmountProvider; + + /** + * @var CategoryResolver + */ + private $categoryResolver; + + /** + * @var WebsiteCategoryProvider + */ + private $websiteCategoryProvider; + + /** + * @var PriceProvider + */ + private $priceProvider; + + /** + * @var \Magento\Setup\Fixtures\AttributeSet\SwatchesGenerator + */ + private $swatchesGenerator; + + /** + * ConfigurableProductsFixture constructor. + * @param FixtureModel $fixtureModel + * @param AttributeSet\AttributeSetFixture $attributeSetsFixture + * @param AttributeSet\Pattern $attributePattern + * @param ProductGenerator $productGenerator + * @param CollectionFactory $attributeCollectionFactory + * @param ConfigurableProductGenerator $configurableProductGenerator + * @param ProductCollectionFactory $collectionFactory + * @param ProductsAmountProvider $productsAmountProvider + * @param CategoryResolver $categoryResolver + * @param WebsiteCategoryProvider $websiteCategoryProvider + * @param PriceProvider $priceProvider + * @param \Magento\Setup\Fixtures\AttributeSet\SwatchesGenerator $swatchesGenerator + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + FixtureModel $fixtureModel, + \Magento\Setup\Fixtures\AttributeSet\AttributeSetFixture $attributeSetsFixture, + \Magento\Setup\Fixtures\AttributeSet\Pattern $attributePattern, + ProductGenerator $productGenerator, + CollectionFactory $attributeCollectionFactory, + ConfigurableProductGenerator $configurableProductGenerator, + ProductCollectionFactory $collectionFactory, + ProductsAmountProvider $productsAmountProvider, + CategoryResolver $categoryResolver, + WebsiteCategoryProvider $websiteCategoryProvider, + PriceProvider $priceProvider, + \Magento\Setup\Fixtures\AttributeSet\SwatchesGenerator $swatchesGenerator + ) { + parent::__construct($fixtureModel); + $this->attributeSetsFixture = $attributeSetsFixture; + $this->attributePattern = $attributePattern; + $this->productGenerator = $productGenerator; + $this->attributeCollectionFactory = $attributeCollectionFactory; + $this->configurableProductGenerator = $configurableProductGenerator; + $this->productCollectionFactory = $collectionFactory; + $this->productsAmountProvider = $productsAmountProvider; + $this->categoryResolver = $categoryResolver; + $this->websiteCategoryProvider = $websiteCategoryProvider; + $this->priceProvider = $priceProvider; + $this->swatchesGenerator = $swatchesGenerator; + } + + /** + * {@inheritdoc} * @SuppressWarnings(PHPMD) - * @return array */ - protected function getHeaders() + public function execute() { - return [ - 'sku', - 'store_view_code', - 'attribute_set_code', - 'product_type', - 'categories', - 'product_websites', - 'color', - 'configurable_variation', - 'cost', - 'country_of_manufacture', - 'created_at', - 'custom_design', - 'custom_design_from', - 'custom_design_to', - 'custom_layout_update', - 'description', - 'enable_googlecheckout', - 'gallery', - 'gift_message_available', - 'gift_wrapping_available', - 'gift_wrapping_price', - 'has_options', - 'image', - 'image_label', - 'is_returnable', - 'manufacturer', - 'meta_description', - 'meta_keyword', - 'meta_title', - 'minimal_price', - 'msrp', - 'msrp_display_actual_price_type', - 'name', - 'news_from_date', - 'news_to_date', - 'options_container', - 'page_layout', - 'price', - 'quantity_and_stock_status', - 'related_tgtr_position_behavior', - 'related_tgtr_position_limit', - 'required_options', - 'short_description', - 'small_image', - 'small_image_label', - 'special_from_date', - 'special_price', - 'special_to_date', - 'product_online', - 'tax_class_name', - 'thumbnail', - 'thumbnail_label', - 'updated_at', - 'upsell_tgtr_position_behavior', - 'upsell_tgtr_position_limit', - 'url_key', - 'url_path', - 'variations', - 'variations_1382710717', - 'variations_1382710773', - 'variations_1382710861', - 'visibility', - 'weight', - 'qty', - 'min_qty', - 'use_config_min_qty', - 'is_qty_decimal', - 'backorders', - 'use_config_backorders', - 'min_sale_qty', - 'use_config_min_sale_qty', - 'max_sale_qty', - 'use_config_max_sale_qty', - 'is_in_stock', - 'notify_stock_qty', - 'use_config_notify_stock_qty', - 'manage_stock', - 'use_config_manage_stock', - 'use_config_qty_increments', - 'qty_increments', - 'use_config_enable_qty_inc', - 'enable_qty_increments', - 'is_decimal_divided', - '_related_sku', - '_related_position', - '_crosssell_sku', - '_crosssell_position', - '_upsell_sku', - '_upsell_position', - '_associated_sku', - '_associated_default_qty', - '_associated_position', - '_tier_price_website', - '_tier_price_customer_group', - '_tier_price_qty', - '_tier_price_price', - '_media_attribute_id', - '_media_image', - '_media_label', - '_media_position', - '_media_is_disabled', - '_super_products_sku', - '_super_attribute_code', - '_super_attribute_option', - 'configurable_variations', - ]; + if (!$this->fixtureModel->getValue('configurable_products', [])) { + return; + } + $simpleProductsCount = $this->fixtureModel->getValue('simple_products', 0); + $maxAmountOfWordsDescription = $this->getSearchConfigValue('max_amount_of_words_description'); + $maxAmountOfWordsShortDescription = $this->getSearchConfigValue('max_amount_of_words_short_description'); + $minAmountOfWordsDescription = $this->getSearchConfigValue('min_amount_of_words_description'); + $minAmountOfWordsShortDescription = $this->getSearchConfigValue('min_amount_of_words_short_description'); + + foreach ($this->getConfigurableProductConfig() as $configurableConfig) { + $configurableSku = $configurableConfig['sku']; + $productAmount = $this->productsAmountProvider->getAmount( + $configurableConfig['products'], + $configurableSku + ); + if (!$productAmount) { + continue; + } + $searchTerms = $this->getSearchTerms(); + $shortDescriptionClosure = $this->getDescriptionClosure( + $searchTerms, + $simpleProductsCount, + $productAmount, + $maxAmountOfWordsShortDescription, + $minAmountOfWordsShortDescription, + 'shortDescription' + ); + $descriptionClosure = $this->getDescriptionClosure( + $searchTerms, + $simpleProductsCount, + $productAmount, + $maxAmountOfWordsDescription, + $minAmountOfWordsDescription, + 'description' + ); + $variationCount = $configurableConfig['variationCount']; + $attributeSet = $configurableConfig['attributeSet']; + $variationSkuClosure = function ($productId, $entityNumber) use ($configurableSku, $variationCount) { + $variationIndex = $this->getConfigurableVariationIndex($entityNumber, $variationCount); + $productId = $this->getConfigurableProductIndex($entityNumber, $variationCount); + + return sprintf($this->getConfigurableOptionSkuPattern($configurableSku), $productId, $variationIndex); + }; + $fixture = [ + 'name' => $variationSkuClosure, + 'sku' => $variationSkuClosure, + 'price' => function ($index, $entityNumber) { + return $this->priceProvider->getPrice($entityNumber); + }, + 'website_ids' => function ($index, $entityNumber) use ($variationCount) { + $configurableIndex = $this->getConfigurableProductIndex($entityNumber, $variationCount); + + return $this->websiteCategoryProvider->getWebsiteIds($configurableIndex); + }, + 'attribute_set_id' => $attributeSet['id'], + 'additional_attributes' => $this->getAdditionalAttributesClosure( + $attributeSet['attributes'], + $variationCount + ), + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + ]; + $this->productGenerator->generate($productAmount * $variationCount, $fixture); + + $skuClosure = function ($productId, $entityNumber) use ($configurableSku) { + return sprintf($configurableSku, $entityNumber + $this->getNewProductStartIndex()); + }; + $fixture = [ + '_variation_sku_pattern' => $this->getFirstVariationSkuPattern($configurableConfig), + '_attributes_count' => count($attributeSet['attributes']), + '_variation_count' => $variationCount, + '_attributes' => $attributeSet['attributes'], + 'type_id' => Configurable::TYPE_CODE, + 'name' => $skuClosure, + 'sku' => $skuClosure, + 'description' => $descriptionClosure, + 'short_description' => $shortDescriptionClosure, + 'attribute_set_id' => $attributeSet['id'], + 'website_ids' => $this->getConfigurableWebsiteIdsClosure(), + 'category_ids' => $configurableConfig['category'], + 'meta_keyword' => $skuClosure, + 'meta_title' => $skuClosure, + ]; + + $this->configurableProductGenerator->generate($productAmount, $fixture); + } } /** - * @param callable $productCategory - * @param callable $productWebsite - * @param string $variation - * @param string $suffix + * @return \Closure + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + private function getConfigurableWebsiteIdsClosure() + { + return function ($index, $entityNumber) { + return $this->websiteCategoryProvider->getWebsiteIds($entityNumber + $this->getNewProductStartIndex()); + }; + } + + /** + * Get product distribution per attribute sets for default attribute sets + * + * @param array $defaultAttributeSets + * @param int $configurableProductsCount * @return array - * @SuppressWarnings(PHPMD) */ - private function generateConfigurableProduct($productCategory, $productWebsite, $variation, $suffix) + private function getDefaultAttributeSetsConfig(array $defaultAttributeSets, $configurableProductsCount) { - return [ - 'sku' => 'Configurable Product %s' . $suffix, - 'store_view_code' => '', - 'attribute_set_code' => 'Default', - 'product_type' => 'configurable', - 'categories' => $productCategory, - 'product_websites' => $productWebsite, - 'color' => '', - 'configurable_variation' => '', - 'cost' => '', - 'country_of_manufacture' => '', - 'created_at' => '2013-10-25 15:12:39', - 'custom_design' => '', - 'custom_design_from' => '', - 'custom_design_to' => '', - 'custom_layout_update' => '', - 'description' => '

    Configurable product description %s

    ', - 'enable_googlecheckout' => '1', - 'gallery' => '', - 'gift_message_available' => '', - 'gift_wrapping_available' => '', - 'gift_wrapping_price' => '', - 'has_options' => '1', - 'image' => '', - 'image_label' => '', - 'is_returnable' => 'no', - 'manufacturer' => '', - 'meta_description' => 'Configurable Product %s

    Configurable product description %s

    ', - 'meta_keyword' => 'Configurable Product %s', - 'meta_title' => 'Configurable Product %s', - 'minimal_price' => '', - 'msrp' => '', - 'msrp_display_actual_price_type' => 'Use config', - 'name' => 'Configurable Product %s' . $suffix, - 'news_from_date' => '', - 'news_to_date' => '', - 'options_container' => 'Block after Info Column', - 'page_layout' => '', - 'price' => '10', - 'quantity_and_stock_status' => 'In Stock', - 'related_tgtr_position_behavior' => '', - 'related_tgtr_position_limit' => '', - 'required_options' => '1', - 'short_description' => '', - 'small_image' => '', - 'small_image_label' => '', - 'special_from_date' => '', - 'special_price' => '', - 'special_to_date' => '', - 'product_online' => '1', - 'tax_class_name' => 'Taxable Goods', - 'thumbnail' => '', - 'thumbnail_label' => '', - 'updated_at' => '2013-10-25 15:12:39', - 'upsell_tgtr_position_behavior' => '', - 'upsell_tgtr_position_limit' => '', - 'url_key' => "configurable-product-%s{$suffix}", - 'url_path' => "configurable-product-%s{$suffix}", - 'visibility' => 'Catalog, Search', - 'weight' => '', - 'qty' => 333, - 'min_qty' => '0.0000', - 'use_config_min_qty' => '1', - 'is_qty_decimal' => '0', - 'backorders' => '0', - 'use_config_backorders' => '1', - 'min_sale_qty' => '1.0000', - 'use_config_min_sale_qty' => '1', - 'max_sale_qty' => '0.0000', - 'use_config_max_sale_qty' => '1', - 'is_in_stock' => '1', - 'notify_stock_qty' => '', - 'use_config_notify_stock_qty' => '1', - 'manage_stock' => '1', - 'use_config_manage_stock' => '1', - 'use_config_qty_increments' => '1', - 'qty_increments' => '0.0000', - 'use_config_enable_qty_inc' => '1', - 'enable_qty_increments' => '0', - 'is_decimal_divided' => '0', - '_related_sku' => '', - '_related_position' => '', - '_crosssell_sku' => '', - '_crosssell_position' => '', - '_upsell_sku' => '', - '_upsell_position' => '', - '_associated_sku' => '', - '_associated_default_qty' => '', - '_associated_position' => '', - '_tier_price_website' => '', - '_tier_price_customer_group' => '', - '_tier_price_qty' => '', - '_tier_price_price' => '', - '_media_attribute_id' => '', - '_media_image' => '', - '_media_label' => '', - '_media_position' => '', - '_media_is_disabled' => '', - 'configurable_variations' => $variation, - ]; + $attributeSetClosure = function ($index) use ($defaultAttributeSets) { + $attributeSetAmount = count(array_keys($defaultAttributeSets)); + mt_srand($index); + + return $attributeSetAmount > ($index - 1) % (int)$this->fixtureModel->getValue('categories', 30) + ? array_keys($defaultAttributeSets)[mt_rand(0, $attributeSetAmount - 1)] + : 'Default'; + }; + $productsPerSet = []; + for ($i = 1; $i <= $configurableProductsCount; $i++) { + $attributeSet = $attributeSetClosure($i); + if (!isset($productsPerSet[$attributeSet])) { + $productsPerSet[$attributeSet] = 0; + } + $productsPerSet[$attributeSet]++; + } + $configurableConfig = []; + foreach ($defaultAttributeSets as $attributeSetName => $attributeSet) { + $skuSuffix = $attributeSetName === 'Default' ? '' : ' - ' . $attributeSetName; + $configurableConfig[] = [ + 'attributeSet' => $attributeSetName, + 'products' => $productsPerSet[$attributeSetName], + 'sku' => 'Configurable Product %s' . $skuSuffix, + ]; + } + + return $configurableConfig; } /** - * Get CSV template rows + * Get sku pattern in format "{configurable-sku}{configurable-index}-option 1" for get associated product ids * - * @param Closure|mixed $productCategory - * @param Closure|mixed $productWebsite + * @param array $configurableConfig + * @see \Magento\Setup\Model\FixtureGenerator\ConfigurableProductTemplateGenerator + * @return string + */ + private function getFirstVariationSkuPattern($configurableConfig) + { + $productIndex = $this->getConfigurableProductIndex(0, $configurableConfig['variationCount']); + + return sprintf($this->getConfigurableOptionSkuPattern($configurableConfig['sku']), $productIndex, 1); + } + + /** + * Get start product index which used in product name, sku, url generation * - * @SuppressWarnings(PHPMD) + * @return int + */ + private function getNewProductStartIndex() + { + if (null === $this->productStartIndex) { + $this->productStartIndex = $this->productCollectionFactory->create() + ->addFieldToFilter('type_id', Configurable::TYPE_CODE) + ->getSize() + 1; + } + + return $this->productStartIndex; + } + + /** + * Get configurable product index number + * + * @param int $entityNumber + * @param int $variationCount + * @return float + */ + private function getConfigurableProductIndex($entityNumber, $variationCount) + { + return floor($entityNumber / $variationCount) + $this->getNewProductStartIndex(); + } + + /** + * Get configurable variation index number + * + * @param int $entityNumber + * @param int $variationCount + * @return float + */ + private function getConfigurableVariationIndex($entityNumber, $variationCount) + { + return $entityNumber % $variationCount + 1; + } + + /** + * {@inheritdoc} + */ + public function getActionTitle() + { + return 'Generating configurable products'; + } + + /** + * {@inheritdoc} + */ + public function introduceParamLabels() + { + return []; + } + + /** + * {@inheritdoc} + */ + public function printInfo(OutputInterface $output) + { + if (!$this->fixtureModel->getValue('configurable_products', [])) { + return ; + } + $configurableProductConfig = $this->getConfigurableProductConfig(); + $generalAmount = array_sum(array_map( + function ($item) { + return $item['products']; + }, + $configurableProductConfig + )); + $output->writeln(sprintf(' |- Configurable products: %s', $generalAmount)); + foreach ($configurableProductConfig as $config) { + $output->writeln( + sprintf( + ' |--- %s products for attribute set "%s"', + $config['products'], + $config['attributeSet']['name'] + ) + ); + } + } + + /** + * Gen default attribute sets with attributes + * @see config/attributeSets.xml * * @return array */ - protected function getRows($productCategory, $productWebsite, $optionsNumber, $suffix = '') + private function getDefaultAttributeSetsWithAttributes() { - $data = []; - $variation = []; - for ($i = 1; $i <= $optionsNumber; $i++) { - $productData = [ - 'sku' => "Configurable Product %s-option {$i}{$suffix}", - 'store_view_code' => '', - 'attribute_set_code' => 'Default', - 'product_type' => 'simple', - 'categories' => $productCategory, - 'product_websites' => $productWebsite, - 'color' => '', - 'configurable_variation' => "option {$i}", - 'cost' => '', - 'country_of_manufacture' => '', - 'created_at' => '2013-10-25 15:12:32', - 'custom_design' => '', - 'custom_design_from' => '', - 'custom_design_to' => '', - 'custom_layout_update' => '', - 'description' => '

    Configurable product description %s

    ', - 'enable_googlecheckout' => '1', - 'gallery' => '', - 'gift_message_available' => '', - 'gift_wrapping_available' => '', - 'gift_wrapping_price' => '', - 'has_options' => '0', - 'image' => '', - 'image_label' => '', - 'is_returnable' => 'no', - 'manufacturer' => '', - 'meta_description' => 'Configurable Product %s

    Configurable product description 1

    ', - 'meta_keyword' => 'Configurable Product 1', - 'meta_title' => 'Configurable Product %s', - 'minimal_price' => '', - 'msrp' => '', - 'msrp_display_actual_price_type' => 'Use config', - 'name' => "Configurable Product {$suffix}- %s-option {$i}", - 'news_from_date' => '', - 'news_to_date' => '', - 'options_container' => 'Block after Info Column', - 'page_layout' => '', - 'price' => function () { return mt_rand(1, 1000) / 10; }, - 'quantity_and_stock_status' => 'In Stock', - 'related_tgtr_position_behavior' => '', - 'related_tgtr_position_limit' => '', - 'required_options' => '0', - 'short_description' => '', - 'small_image' => '', - 'small_image_label' => '', - 'special_from_date' => '', - 'special_price' => '', - 'special_to_date' => '', - 'product_online' => '1', - 'tax_class_name' => 'Taxable Goods', - 'thumbnail' => '', - 'thumbnail_label' => '', - 'updated_at' => '2013-10-25 15:12:32', - 'upsell_tgtr_position_behavior' => '', - 'upsell_tgtr_position_limit' => '', - 'url_key' => "simple-of-configurable-product-{$suffix}-%s-option-{$i}", - 'url_path' => "simple-of-configurable-product-{$suffix}-%s-option-{$i}", - 'variations' => '', - 'variations_1382710717' => '', - 'variations_1382710773' => '', - 'variations_1382710861' => '', - 'visibility' => 'Not Visible Individually', - 'weight' => '1', - 'qty' => '111.0000', - 'min_qty' => '0.0000', - 'use_config_min_qty' => '1', - 'is_qty_decimal' => '0', - 'backorders' => '0', - 'use_config_backorders' => '1', - 'min_sale_qty' => '1.0000', - 'use_config_min_sale_qty' => '1', - 'max_sale_qty' => '0.0000', - 'use_config_max_sale_qty' => '1', - 'is_in_stock' => '1', - 'notify_stock_qty' => '', - 'use_config_notify_stock_qty' => '1', - 'manage_stock' => '1', - 'use_config_manage_stock' => '1', - 'use_config_qty_increments' => '1', - 'qty_increments' => '0.0000', - 'use_config_enable_qty_inc' => '1', - 'enable_qty_increments' => '0', - 'is_decimal_divided' => '0', - '_related_sku' => '', - '_related_position' => '', - '_crosssell_sku' => '', - '_crosssell_position' => '', - '_upsell_sku' => '', - '_upsell_position' => '', - '_associated_sku' => '', - '_associated_default_qty' => '', - '_associated_position' => '', - '_tier_price_website' => '', - '_tier_price_customer_group' => '', - '_tier_price_qty' => '', - '_tier_price_price' => '', - '_media_attribute_id' => '', - '_media_image' => '', - '_media_label' => '', - '_media_position' => '', - '_media_is_disabled' => '', - ]; + $attributeSets = $this->fixtureModel->getValue('attribute_sets', null); + $attributeSetData = []; - $variation[] = implode( - ',', + if ($attributeSets !== null && array_key_exists('attribute_set', $attributeSets)) { + foreach ($attributeSets['attribute_set'] as $attributeSet) { + $attributesData = array_key_exists(0, $attributeSet['attributes']['attribute']) + ? $attributeSet['attributes']['attribute'] : [$attributeSet['attributes']['attribute']]; + $attributes = []; + foreach ($attributesData as $attributeData) { + $values = []; + $optionsData = array_key_exists(0, $attributeData['options']['option']) + ? $attributeData['options']['option'] : [$attributeData['options']['option']]; + foreach ($optionsData as $optionData) { + $values[] = $optionData['label']; + } + + $attributes[] = ['name' => $attributeData['attribute_code'], 'values' => $values]; + } + $attributeSetData[$attributeSet['name']] = [ + 'name' => $attributeSet['name'], + 'attributes' => $attributes + ]; + } + } + + $attributeOptions = range(1, $this->getConfigurableProductsVariationsValue()); + array_walk( + $attributeOptions, + function (&$item, $key) { + $item = 'option ' . ($key + 1); + } + ); + $attributeSetData['Default'] = [ + 'name' => 'Default', + 'attributes' => [ [ - 'sku=' . $productData['sku'], - 'configurable_variation=' . $productData['configurable_variation'], + 'name' => 'configurable_variation', + 'values' => $attributeOptions ] + ] + ]; + + return $attributeSetData; + } + + /** + * Get configurable product configuration for generate products per attribute set + * + * @return array + * @throws ValidatorException + */ + private function getConfigurableProductConfig() + { + $defaultAttributeSets = $this->getDefaultAttributeSetsWithAttributes(); + $configurableProductConfig = $this->prepareConfigurableConfig($defaultAttributeSets); + + $configurableProductConfig = array_map(function ($config) { + return array_merge( + [ + 'attributeSet' => null, + 'attributes' => null, + 'options' => null, + 'sku' => null, + 'category' => null, + 'swatches' => null, + ], + $config + ); + }, $configurableProductConfig); + + $skuPull = []; + foreach ($configurableProductConfig as $i => &$config) { + $attributeSet = $config['attributeSet']; + $attributes = (int)$config['attributes']; + $options = (int)$config['options']; + if ($attributeSet && isset($defaultAttributeSets[$attributeSet])) { + // process default attribute sets + $attributeSet = $defaultAttributeSets[$attributeSet]; + $attributes = count($attributeSet['attributes']); + $options = count($attributeSet['attributes'][0]['values']); + } elseif ($attributes && $options) { + // process dynamic attribute sets + $attributeSet = $this->getAttributeSet($attributes, $options, $config['swatches']); + } + // do not process if any required option is missed + if (count(array_filter([$attributeSet, $attributes, $options])) !== 3) { + unset($configurableProductConfig[$i]); + continue; + } + + $config['sku'] = $this->getConfigurableSkuPattern($config, $attributeSet['name']); + $config['category'] = $this->getConfigurableCategory($config); + $config['attributeSet'] = $this->convertAttributesToDBFormat($attributeSet); + $config['attributes'] = $attributes; + $config['options'] = $options; + $config['variationCount'] = pow($options, $attributes); + $skuPull[] = $config['sku']; + } + + if (count($skuPull) !== count(array_unique($skuPull))) { + throw new ValidatorException( + __('Sku pattern for configurable product must be unique per attribute set') ); - $data[] = $productData; } - $data[] = $this->generateConfigurableProduct( - $productCategory, - $productWebsite, - implode('|', $variation), - $suffix - ); - return $data; + return $configurableProductConfig; } /** - * {@inheritdoc} + * Prepare configuration. If amount of configurable products set in profile then return predefined attribute sets + * else return configuration from profile + * + * @param array $defaultAttributeSets + * @return array + * @throws ValidatorException */ - public function execute() + private function prepareConfigurableConfig($defaultAttributeSets) { - $configurableCount = $this->fixtureModel->getValue('configurable_products', 0); - if (!$configurableCount) { - return; + $configurableConfigs = $this->fixtureModel->getValue('configurable_products', []); + $configurableConfigs = is_array($configurableConfigs) ? $configurableConfigs : (int)$configurableConfigs; + if (is_int($configurableConfigs)) { + $configurableConfigs = $this->getDefaultAttributeSetsConfig($defaultAttributeSets, $configurableConfigs); + } elseif (isset($configurableConfigs['config'])) { + if (!isset($configurableConfigs['config'][0])) { + // in case when one variation is passed + $configurableConfigs = [$configurableConfigs['config']]; + } else { + $configurableConfigs = $configurableConfigs['config']; + } + } else { + throw new ValidatorException(__('Configurable product config is invalid')); } - $this->fixtureModel->resetObjectManager(); - $result = $this->getCategoriesAndWebsites(); + return $configurableConfigs; + } - $result = array_values($result); + /** + * @param array $config + * @return \Closure + */ + private function getConfigurableCategory($config) + { + if (isset($config['category'])) { + return function ($index, $entityNumber) use ($config) { + $websiteClosure = $this->getConfigurableWebsiteIdsClosure(); + $websites = $websiteClosure($index, $entityNumber); - $productWebsite = function ($index) use ($result) { - return $result[$index % count($result)][0]; - }; - $productCategory = function ($index) use ($result) { - return $result[$index % count($result)][2] . '/' . $result[$index % count($result)][1]; - }; + return $this->categoryResolver->getCategory( + array_shift($websites), + $config['category'] + ); + }; + } else { + return function ($index, $entityNumber) { + return $this->websiteCategoryProvider->getCategoryId($entityNumber); + }; + } + } - /** - * Create configurable products - */ - $pattern = new Pattern(); - $pattern->setHeaders($this->getHeaders()); - $pattern->setRowsSet( - $this->getRows( - $productCategory, - $productWebsite, - $this->fixtureModel->getValue('configurable_products_variation', 3) - ) - ); + /** + * @param array $config + * @param string $attributeSetName + * @return string + */ + private function getConfigurableSkuPattern($config, $attributeSetName) + { + $sku = isset($config['sku']) ? $config['sku'] : null; + if (!$sku) { + $sku = 'Product ' . $attributeSetName . ' %s'; + } + if (false === strpos($sku, '%s')) { + $sku .= ' %s'; + } - /** @var \Magento\ImportExport\Model\Import $import */ - $import = $this->fixtureModel->getObjectManager()->create( - \Magento\ImportExport\Model\Import::class, - [ - 'data' => [ - 'entity' => 'catalog_product', - 'behavior' => 'append', - 'validation_strategy' => 'validation-stop-on-errors', - ], - ] - ); + return $sku; + } + + /** + * @param int $attributes + * @param int $options + * @param string $swatches + * @return array + */ + private function getAttributeSet($attributes, $options, $swatches) + { + $attributeCode = 'configurable_attribute' . $attributes . '_' . $options . '_'; + $setName = 'Dynamic Attribute Set ' . $attributes . '-' . $options; - $source = $this->fixtureModel->getObjectManager()->create( - Generator::class, - ['rowPattern' => $pattern, 'count' => $configurableCount] + return $this->attributeSetsFixture->createAttributeSet( + $this->attributePattern->generateAttributeSet( + $setName, + $attributes, + $options, + function ($index, $attribute) use ($attributeCode, $options, $swatches) { + $data = [ + 'attribute_code' => $attributeCode . $index, + 'frontend_label' => 'Big Attribute ' . $attributeCode . $index, + ]; + return array_replace_recursive( + $attribute, + $data, + $this->swatchesGenerator->generateSwatchData($options, $attributeCode . $index, $swatches) + ); + } + ) ); - // it is not obvious, but the validateSource() will actually save import queue data to DB - if (!$import->validateSource($source)) { - throw new \Exception($import->getFormatedLogTrace()); + } + + /** + * @return array + */ + private function getSearchConfig() + { + if (!$this->searchConfig) { + $this->searchConfig = $this->fixtureModel->getValue('search_config', null); } - // this converts import queue into actual entities - if (!$import->importSource()) { - throw new \Exception($import->getFormatedLogTrace()); + return $this->searchConfig; + } + + /** + * @param string $name + * @return int|mixed + */ + private function getSearchConfigValue($name) + { + return $this->getSearchConfig() === null + ? 0 : ($this->getSearchConfig()[$name] === null ? 0 : $this->getSearchConfig()[$name]); + } + + /** + * @return array + */ + private function getSearchTerms() + { + $searchTerms = $this->fixtureModel->getValue('search_terms', null); + if ($searchTerms !== null) { + $searchTerms = array_key_exists(0, $searchTerms['search_term']) + ? $searchTerms['search_term'] : [$searchTerms['search_term']]; } + return $searchTerms; } - // @codingStandardsIgnoreEnd /** - * {@inheritdoc} + * Get configurable products variations value. + * + * @return int */ - public function getActionTitle() + private function getConfigurableProductsVariationsValue() { - return 'Generating configurable products'; + return $this->fixtureModel->getValue('configurable_products_variation', 3); } /** - * {@inheritdoc} + * Get additional attributes closure. + * + * @param array $attributes + * @param int $variationCount + * @return \Closure + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - public function introduceParamLabels() + private function getAdditionalAttributesClosure(array $attributes, $variationCount) { - return [ - 'configurable_products' => 'Configurable products', - ]; + return function ($attributeSetId, $index, $entityNumber) use ($attributes, $variationCount) { + + $variationIndex = $this->getConfigurableVariationIndex($entityNumber, $variationCount) - 1; + $attributeValues = []; + $optionsPerAttribute = count($attributes[0]['values']); + $variationIndex = $variationIndex % $variationCount; + + $variationsMatrix = $this->generateVariationsMatrix(count($attributes), $optionsPerAttribute); + if (isset($variationsMatrix[$variationIndex])) { + $tempProductData = []; + foreach ($variationsMatrix[$variationIndex] as $attributeIndex => $optionIndex) { + $attributeCode = $attributes[$attributeIndex]['name']; + $option = $attributes[$attributeIndex]['values'][$optionIndex]; + $tempProductData[$attributeCode] = $option; + } + return $tempProductData; + } + + return $attributeValues; + }; } /** + * Generates matrix of all possible variations. + * @param int $attributesPerSet + * @param int $optionsPerAttribute * @return array */ - private function getCategoriesAndWebsites() + private function generateVariationsMatrix($attributesPerSet, $optionsPerAttribute) { - /** @var \Magento\Store\Model\StoreManager $storeManager */ - $storeManager = $this->fixtureModel->getObjectManager()->get(\Magento\Store\Model\StoreManager::class); - /** @var $category \Magento\Catalog\Model\Category */ - $category = $this->fixtureModel->getObjectManager()->create(\Magento\Catalog\Model\Category::class); + $variationsMatrix = null; + for ($i = 1; $i <= $attributesPerSet; $i++) { + $variationsMatrix[] = range(0, $optionsPerAttribute - 1); + } + return $this->generateVariations($variationsMatrix); + } + /** + * Build all possible variations based on attributes and options count. + * @param array|null $variationsMatrix + * @return array + */ + private function generateVariations($variationsMatrix) + { + if (!$variationsMatrix) { + return [[]]; + } + $set = array_shift($variationsMatrix); + $subset = $this->generateVariations($variationsMatrix); $result = []; - //Get all websites - $websites = $storeManager->getWebsites(); - foreach ($websites as $website) { - $websiteCode = $website->getCode(); - //Get all groups - $websiteGroups = $website->getGroups(); - foreach ($websiteGroups as $websiteGroup) { - $websiteGroupRootCategory = $websiteGroup->getRootCategoryId(); - $category->load($websiteGroupRootCategory); - $categoryResource = $category->getResource(); - $rootCategoryName = $category->getName(); - //Get all categories - $resultsCategories = $categoryResource->getAllChildren($category); - foreach ($resultsCategories as $resultsCategory) { - $category->load($resultsCategory); - $structure = explode('/', $category->getPath()); - $pathSize = count($structure); - if ($pathSize > 1) { - $path = []; - for ($i = 1; $i < $pathSize; $i++) { - $path[] = $category->load($structure[$i])->getName(); - } - array_shift($path); - $resultsCategoryName = implode('/', $path); - } else { - $resultsCategoryName = $category->getName(); - } - //Deleted root categories - if (trim($resultsCategoryName) != '') { - $result[$resultsCategory] = [$websiteCode, $resultsCategoryName, $rootCategoryName]; + foreach ($set as $value) { + foreach ($subset as $subValue) { + array_unshift($subValue, $value); + $result[] = $subValue; + } + } + return $result; + } + + /** + * Get configurable option sku pattern. + * + * @param string $skuPattern + * @return string + */ + private function getConfigurableOptionSkuPattern($skuPattern) + { + return $skuPattern . ' - option %s'; + } + + /** + * @param array|null $searchTerms + * @param int $simpleProductsCount + * @param int $configurableProductsCount + * @param int $maxAmountOfWordsDescription + * @param int $minAmountOfWordsDescription + * @param string $descriptionPrefix + * @return \Closure + */ + private function getDescriptionClosure( + $searchTerms, + $simpleProductsCount, + $configurableProductsCount, + $maxAmountOfWordsDescription, + $minAmountOfWordsDescription, + $descriptionPrefix = 'description' + ) { + if (null === $this->dataGenerator) { + $fileName = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'dictionary.csv'; + $this->dataGenerator = new DataGenerator(realpath($fileName)); + } + + return function ($index) use ( + $searchTerms, + $simpleProductsCount, + $configurableProductsCount, + $maxAmountOfWordsDescription, + $minAmountOfWordsDescription, + $descriptionPrefix + ) { + $count = $searchTerms === null + ? 0 + : round( + $searchTerms[$index % count($searchTerms)]['count'] * ( + $configurableProductsCount / ($simpleProductsCount + $configurableProductsCount) + ) + ); + mt_srand($index); + return $this->dataGenerator->generate( + $minAmountOfWordsDescription, + $maxAmountOfWordsDescription, + $descriptionPrefix . '-' . $index + ) . + ($index <= ($count * count($searchTerms)) ? ' ' . + $searchTerms[$index % count($searchTerms)]['term'] : ''); + }; + } + + /** + * Convert attribute set data to db format for use in simple product generation + * + * @param array $attributeSet + * @return array + */ + private function convertAttributesToDBFormat(array $attributeSet) + { + $attributeSetName = $attributeSet['name']; + $attributeSetId = null; + $attributes = []; + foreach ($attributeSet['attributes'] as $attributeData) { + $attributeCollection = $this->attributeCollectionFactory->create(); + + $attributeCollection->setAttributeSetFilterBySetName($attributeSetName, Product::ENTITY); + $attributeCollection->addFieldToFilter( + 'attribute_code', + $attributeData['name'] + ); + /** @var \Magento\Eav\Model\Entity\Attribute $attribute */ + foreach ($attributeCollection as $attribute) { + $attributeSetId = $attribute->getAttributeSetId(); + $values = []; + $options = $attribute->getOptions(); + foreach ($options ?: [] as $option) { + if ($option->getValue()) { + $values[] = $option->getValue(); } } + $attributes[] = + [ + 'name' => $attribute->getAttributeCode(), + 'id' => $attribute->getAttributeId(), + 'values' => $values + ]; } } - return $result; + + return ['id' => $attributeSetId, 'name' => $attributeSetName, 'attributes' => $attributes]; } } diff --git a/setup/src/Magento/Setup/Fixtures/CustomerGroupsFixture.php b/setup/src/Magento/Setup/Fixtures/CustomerGroupsFixture.php new file mode 100644 index 0000000000000..327f597b69930 --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/CustomerGroupsFixture.php @@ -0,0 +1,92 @@ +groupCollectionFactory = $groupCollectionFactory; + $this->groupRepository = $groupRepository; + $this->groupFactory = $groupFactory; + } + + /** + * @inheritdoc + */ + public function execute() + { + $existingCustomerGroupsCount = $this->groupCollectionFactory->create()->getSize(); + $customerGroupsCount = $this->fixtureModel->getValue('customer_groups', 0); + if ($customerGroupsCount < 1) { + return; + } + + for ($i = $existingCustomerGroupsCount; $i < $customerGroupsCount; ++$i) { + $groupDataObject = $this->groupFactory->create(); + $groupDataObject + ->setCode('customer_group_' . $i) + ->setTaxClassId(self::DEFAULT_TAX_CLASS_ID); + $this->groupRepository->save($groupDataObject); + } + } + + /** + * @inheritdoc + */ + public function getActionTitle() + { + return 'Generating customer groups'; + } + + /** + * @inheritdoc + */ + public function introduceParamLabels() + { + return [ + 'customer_groups' => 'Customer groups' + ]; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/CustomersFixture.php b/setup/src/Magento/Setup/Fixtures/CustomersFixture.php index ca29b4cf70447..c81795d87d980 100644 --- a/setup/src/Magento/Setup/Fixtures/CustomersFixture.php +++ b/setup/src/Magento/Setup/Fixtures/CustomersFixture.php @@ -1,111 +1,121 @@ {customers amount} + * Customers will have normal distribution on all available websites + * + * Each customer will have absolutely the same data + * except customer email, customer group and customer addresses + * + * @see \Magento\Setup\Model\FixtureGenerator\CustomerTemplateGenerator + * to view general customer data + * + * @see \Magento\Setup\Model\Customer\CustomerDataGenerator + * if you need dynamically change data per each customer + * + * @see \Magento\Setup\Model\Address\AddressDataGenerator + * if you need dynamically change address data per each customer + * + * @see setup/performance-toolkit/config/customerConfig.xml + * here you can change amount of addresses to be generated per each customer + * Supports the following format: + * + * {amount of addresses} + * + * + * @see setup/performance-toolkit/profiles/ce/small.xml */ class CustomersFixture extends Fixture { /** * @var int */ - protected $priority = 60; + protected $priority = 70; + + /** + * @var CustomerGenerator + */ + private $customerGenerator; + + /** + * @var CustomerDataGeneratorFactory + */ + private $customerDataGeneratorFactory; + + /** + * @var array + */ + private $defaultCustomerConfig = [ + 'addresses-count' => 2 + ]; + + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * @param FixtureModel $fixtureModel + * @param CustomerGenerator $customerGenerator + * @param CustomerDataGeneratorFactory $customerDataGeneratorFactory + * @param CollectionFactory $collectionFactory + */ + public function __construct( + FixtureModel $fixtureModel, + CustomerGenerator $customerGenerator, + CustomerDataGeneratorFactory $customerDataGeneratorFactory, + CollectionFactory $collectionFactory + ) { + parent::__construct($fixtureModel); + + $this->customerGenerator = $customerGenerator; + $this->customerDataGeneratorFactory = $customerDataGeneratorFactory; + $this->collectionFactory = $collectionFactory; + } /** * {@inheritdoc} */ public function execute() { - $customersNumber = $this->fixtureModel->getValue('customers', 0); + $customersNumber = $this->getCustomersAmount(); if (!$customersNumber) { return; } - $this->fixtureModel->resetObjectManager(); - - /** @var \Magento\Store\Model\StoreManager $storeManager */ - $storeManager = $this->fixtureModel->getObjectManager()->create(\Magento\Store\Model\StoreManager::class); - /** @var $defaultStoreView \Magento\Store\Model\Store */ - $defaultStoreView = $storeManager->getDefaultStoreView(); - $defaultStoreViewId = $defaultStoreView->getStoreId(); - $defaultStoreViewCode = $defaultStoreView->getCode(); - - $result = []; - //Get all websites - $websites = $storeManager->getWebsites(); - foreach ($websites as $website) { - $result[] = $website->getCode(); - } - $result = array_values($result); - - $productWebsite = function ($index) use ($result) { - return $result[$index % count($result)]; - }; - - $pattern = [ - 'email' => 'user_%s@example.com', - '_website' => $productWebsite, - '_store' => $defaultStoreViewCode, - 'confirmation' => null, - 'created_at' => '30-08-2012 17:43', - 'created_in' => 'Default', - 'default_billing' => '1', - 'default_shipping' => '1', - 'disable_auto_group_change' => '0', - 'dob' => '12-10-1991', - 'firstname' => 'Firstname', - 'gender' => 'Male', - 'group_id' => '1', - 'lastname' => 'Lastname', - 'middlename' => '', - 'password_hash' => '', - 'prefix' => null, - 'rp_token' => null, - 'rp_token_created_at' => null, - 'store_id' => $defaultStoreViewId, - 'suffix' => null, - 'taxvat' => null, - 'website_id' => '1', - 'password' => '123123q', - '_address_city' => 'Fayetteville', - '_address_company' => '', - '_address_country_id' => 'US', - '_address_fax' => '', - '_address_firstname' => 'Anthony', - '_address_lastname' => 'Nealy', - '_address_middlename' => '', - '_address_postcode' => '123123', - '_address_prefix' => '', - '_address_region' => 'Arkansas', - '_address_street' => '123 Freedom Blvd. #123', - '_address_suffix' => '', - '_address_telephone' => '022-333-4455', - '_address_vat_id' => '', - '_address_default_billing_' => '1', - '_address_default_shipping_' => '1', - ]; - $generator = new Generator($pattern, $customersNumber); - /** @var \Magento\ImportExport\Model\Import $import */ - $import = $this->fixtureModel->getObjectManager()->create( - \Magento\ImportExport\Model\Import::class, - [ - 'data' => [ - 'entity' => 'customer_composite', - 'behavior' => 'append', - 'validation_strategy' => 'validation-stop-on-errors' - ] - ] + + /** @var CustomerDataGenerator $customerDataGenerator */ + $customerDataGenerator = $this->customerDataGeneratorFactory->create( + $this->getCustomersConfig() ); - // it is not obvious, but the validateSource() will actually save import queue data to DB - $import->validateSource($generator); - // this converts import queue into actual entities - $import->importSource(); + + $fixtureMap = [ + 'customer_data' => function ($customerId) use ($customerDataGenerator) { + return $customerDataGenerator->generate($customerId); + }, + ]; + + $this->customerGenerator->generate($customersNumber, $fixtureMap); + } + + /** + * @return int + */ + private function getCustomersAmount() + { + return max(0, $this->fixtureModel->getValue('customers', 0) - $this->collectionFactory->create()->getSize()); } /** @@ -125,4 +135,12 @@ public function introduceParamLabels() 'customers' => 'Customers' ]; } + + /** + * @return array + */ + private function getCustomersConfig() + { + return $this->fixtureModel->getValue('customer-config', $this->defaultCustomerConfig); + } } diff --git a/setup/src/Magento/Setup/Fixtures/EavVariationsFixture.php b/setup/src/Magento/Setup/Fixtures/EavVariationsFixture.php index ccbbfefba0968..ed731b8da6712 100644 --- a/setup/src/Magento/Setup/Fixtures/EavVariationsFixture.php +++ b/setup/src/Magento/Setup/Fixtures/EavVariationsFixture.php @@ -1,13 +1,21 @@ eavConfig = $eavConfig; + $this->cache = $cache; + $this->storeManager = $storeManager; + $this->attributeSet = $attributeSet; + $this->attributeFactory = $attributeFactory; + } + /** * {@inheritdoc} */ public function execute() { - $configurablesCount = $this->fixtureModel->getValue('configurable_products', 0); - if (!$configurablesCount) { + if (!$this->fixtureModel->getValue('configurable_products', []) + || in_array($this->getAttributeCode(), $this->eavConfig->getEntityAttributeCodes(Product::ENTITY))) { return; } - $this->fixtureModel->resetObjectManager(); - $this->generateAttribute('', $this->fixtureModel->getValue('configurable_products_variation', 3)); - /* @var $cache \Magento\Framework\App\CacheInterface */ - $cache = $this->fixtureModel->getObjectManager()->get(\Magento\Framework\App\CacheInterface::class); + $this->generateAttribute($this->fixtureModel->getValue('configurable_products_variation', 3)); - $cacheKey = \Magento\Eav\Model\Config::ATTRIBUTES_CACHE_ID . \Magento\Catalog\Model\Product::ENTITY; - $cache->remove($cacheKey); + $cacheKey = Config::ATTRIBUTES_CACHE_ID . Product::ENTITY; + $this->cache->remove($cacheKey); } /** @@ -54,18 +109,12 @@ public function introduceParamLabels() } /** - * @param int|string $index * @param int $optionCount * @return void */ - private function generateAttribute($index, $optionCount) + private function generateAttribute($optionCount) { - /* @var $model \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ - $model = $this->fixtureModel->getObjectManager() - ->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); - /** @var \Magento\Store\Model\StoreManager $storeManager */ - $storeManager = $this->fixtureModel->getObjectManager()->create(\Magento\Store\Model\StoreManager::class); - $stores = $storeManager->getStores(); + $stores = $this->storeManager->getStores(); $storeViewsCount = count($stores); $options = []; @@ -81,17 +130,17 @@ private function generateAttribute($index, $optionCount) 'is_required' => '0', 'option' => $options, 'default' => ['option_0'], - 'attribute_code' => 'configurable_variation' . $index, + 'attribute_code' => $this->getAttributeCode(), 'is_global' => '1', 'default_value_text' => '', 'default_value_yesno' => '0', 'default_value_date' => '', 'default_value_textarea' => '', 'is_unique' => '0', - 'is_searchable' => '0', + 'is_searchable' => '1', 'is_visible_in_advanced_search' => '0', 'is_comparable' => '0', - 'is_filterable' => '0', + 'is_filterable' => '1', 'is_filterable_in_search' => '0', 'is_used_for_promo_rules' => '0', 'is_html_allowed_on_front' => '1', @@ -105,15 +154,37 @@ private function generateAttribute($index, $optionCount) 'entity_type_id' => 4, 'is_user_defined' => 1, ]; + + $data['swatch_input_type'] = Swatch::SWATCH_INPUT_TYPE_VISUAL; + $data['swatchvisual']['value'] = array_reduce( + range(1, $optionCount), + function ($values, $index) use ($optionCount) { + $values['option_' . $index] = '#' + . str_repeat( + dechex(255 * $index / $optionCount), + 3 + ); + return $values; + }, + [] + ); + $data['optionvisual']['value'] = array_reduce( + range(1, $optionCount), + function ($values, $index) use ($optionCount) { + $values['option_' . $index] = ['option ' . $index]; + return $values; + }, + [] + ); + /** * The logic is not obvious, but looking to the controller logic for configurable products this attribute * requires to be saved twice to become a child of Default attribute set and become available for creating * and|or importing configurable products. * See MAGETWO-16492 */ - $model->addData($data); - $attributeSet = $this->fixtureModel->getObjectManager()->get(\Magento\Eav\Model\Entity\Attribute\Set::class); - $attributeSet->load(self::ATTRIBUTE_SET_ID); + $model = $this->attributeFactory->create(['data' => $data]); + $attributeSet = $this->attributeSet->load(self::ATTRIBUTE_SET_ID); $model->setAttributeSetId(self::ATTRIBUTE_SET_ID); $model->setAttributeGroupId($attributeSet->getDefaultGroupId(4)); @@ -122,4 +193,12 @@ private function generateAttribute($index, $optionCount) $model->setAttributeSetId(self::ATTRIBUTE_SET_ID); $model->save(); } + + /** + * @return string + */ + private function getAttributeCode() + { + return 'configurable_variation'; + } } diff --git a/setup/src/Magento/Setup/Fixtures/Fixture.php b/setup/src/Magento/Setup/Fixtures/Fixture.php index a4c7aa33b9b89..796c1ee8625c0 100644 --- a/setup/src/Magento/Setup/Fixtures/Fixture.php +++ b/setup/src/Magento/Setup/Fixtures/Fixture.php @@ -1,13 +1,15 @@ introduceParamLabels() as $configName => $label) { + $configValue = $this->fixtureModel->getValue($configName); + $generationCount = is_array($configValue) === true + ? count($configValue[array_keys($configValue)[0]]) + : $configValue; + $output->writeln(' |- ' . $label . ': ' . $generationCount .''); + } + } + /** * Introduce parameters labels * diff --git a/setup/src/Magento/Setup/Fixtures/FixtureConfig.php b/setup/src/Magento/Setup/Fixtures/FixtureConfig.php new file mode 100644 index 0000000000000..e8052253113fd --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/FixtureConfig.php @@ -0,0 +1,75 @@ +parser = $parser; + } + + /** + * Load config from file + * + * @param string $filename + * @throws \Exception + * + * @return void + */ + public function loadConfig($filename) + { + if (!is_readable($filename)) { + throw new \Exception("Profile configuration file `{$filename}` is not readable or does not exists."); + } + $this->parser->getDom()->load($filename); + $this->parser->getDom()->xinclude(); + $this->config = $this->parser->xmlToArray(); + $this->config['config']['profile']['di'] = dirname($filename) . '/' + . (isset($this->config['config']['profile']['di']) + ? $this->config['config']['profile']['di'] + : '../../config/di.xml' + ); + } + + /** + * Get profile configuration value + * + * @param string $key + * @param null|mixed $default + * + * @return mixed + */ + public function getValue($key, $default = null) + { + return isset($this->config['config']['profile'][$key]) ? + ( + // Work around for how attributes are handled in the XML parser when injected via xinclude due to the + // files existing outside of the current working directory. + isset($this->config['config']['profile'][$key]['_value']) ? + $this->config['config']['profile'][$key]['_value'] : $this->config['config']['profile'][$key] + ) : $default; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/FixtureModel.php b/setup/src/Magento/Setup/Fixtures/FixtureModel.php index 118b29cb4306b..e9feed1193314 100644 --- a/setup/src/Magento/Setup/Fixtures/FixtureModel.php +++ b/setup/src/Magento/Setup/Fixtures/FixtureModel.php @@ -1,6 +1,6 @@ initArguments = $initArguments; $this->reindexCommand = $reindexCommand; - $this->fileParser = $fileParser; } /** @@ -114,14 +103,16 @@ public function loadFixtures() $file = basename($file, '.php'); /** @var \Magento\Setup\Fixtures\Fixture $fixture */ $type = 'Magento\Setup\Fixtures' . '\\' . $file; - $fixture = new $type($this); + $fixture = $this->getObjectManager()->create( + $type, + [ + 'fixtureModel' => $this, + ] + ); $this->fixtures[$fixture->getPriority()] = $fixture; } ksort($this->fixtures); - foreach ($this->fixtures as $fixture) { - $this->paramLabels = array_merge($this->paramLabels, $fixture->introduceParamLabels()); - } return $this; } @@ -129,6 +120,7 @@ public function loadFixtures() * Get param labels * * @return array + * @deprecated */ public function getParamLabels() { @@ -160,24 +152,38 @@ public function getObjectManager() $this->objectManager = $objectManagerFactory->create($this->initArguments); $this->objectManager->get(\Magento\Framework\App\State::class)->setAreaCode(self::AREA_CODE); } + return $this->objectManager; } /** - * Init Object Manager + * Init Object Manager * + * @param string $area * @return FixtureModel */ - public function initObjectManager() + public function initObjectManager($area = self::AREA_CODE) { - $this->getObjectManager() - ->configure( - $this->getObjectManager() - ->get(\Magento\Framework\ObjectManager\ConfigLoaderInterface::class) - ->load(self::AREA_CODE) + $objectManger = $this->getObjectManager(); + $configuration = $objectManger + ->get(\Magento\Framework\ObjectManager\ConfigLoaderInterface::class) + ->load($area); + $objectManger->configure($configuration); + + $diConfiguration = $this->getValue('di'); + if (file_exists($diConfiguration)) { + $dom = new \DOMDocument(); + $dom->load($diConfiguration); + + $objectManger->configure( + $objectManger + ->get(\Magento\Framework\ObjectManager\Config\Mapper\Dom::class) + ->convert($dom) ); - $this->getObjectManager()->get(\Magento\Framework\Config\ScopeInterface::class) - ->setCurrentScope(self::AREA_CODE); + } + + $objectManger->get(\Magento\Framework\Config\ScopeInterface::class) + ->setCurrentScope($area); return $this; } @@ -185,14 +191,25 @@ public function initObjectManager() * Reset object manager * * @return \Magento\Framework\ObjectManagerInterface + * @deprecated */ public function resetObjectManager() { - $this->objectManager = null; - $this->initObjectManager(); return $this; } + /** + * @return FixtureConfig + */ + private function getConfig() + { + if (null === $this->config) { + $this->config = $this->getObjectManager()->get(FixtureConfig::class); + } + + return $this->config; + } + /** * Load config from file * @@ -203,10 +220,7 @@ public function resetObjectManager() */ public function loadConfig($filename) { - if (!is_readable($filename)) { - throw new \Exception("Profile configuration file `{$filename}` is not readable or does not exists."); - } - $this->config = $this->fileParser->load($filename)->xmlToArray(); + return $this->getConfig()->loadConfig($filename); } /** @@ -219,6 +233,6 @@ public function loadConfig($filename) */ public function getValue($key, $default = null) { - return isset($this->config['config']['profile'][$key]) ? $this->config['config']['profile'][$key] : $default; + return $this->getConfig()->getValue($key, $default); } } diff --git a/setup/src/Magento/Setup/Fixtures/IndexersStatesApplyFixture.php b/setup/src/Magento/Setup/Fixtures/IndexersStatesApplyFixture.php index 9a80889e9c232..f6cc76364f343 100644 --- a/setup/src/Magento/Setup/Fixtures/IndexersStatesApplyFixture.php +++ b/setup/src/Magento/Setup/Fixtures/IndexersStatesApplyFixture.php @@ -1,6 +1,6 @@ storeManager = $storeManager; + $this->productCollectionFactory = $productCollectionFactory; + $this->productRepository = $productRepository; + $this->optionRepository = $optionRepository; + $this->linkManagement = $linkManagement; + $this->serializer = $serializer; + parent::__construct($fixtureModel); + } + + /** + * @inheritdoc + * + * Design of Performance Fixture Generators require generator classes to override Fixture Model's execute method. + * + * @throws \Exception Any exception raised during DB query. + * @return void * @SuppressWarnings(PHPMD) */ public function execute() { - $ordersCount = $this->fixtureModel->getValue('orders', 0); - if ($ordersCount < 1) { - return; - } - $this->fixtureModel->resetObjectManager(); - - $connection = $this->getConnection(); - - $quoteTableName = $this->getTableName( - 'quote', - \Magento\Quote\Model\ResourceModel\Quote::class - ); - $quoteAddressTableName = $this->getTableName( - 'quote_address', - \Magento\Quote\Model\ResourceModel\Quote\Address::class + $orderSimpleCountFrom = (int)$this->fixtureModel->getValue( + 'order_simple_product_count_from', + self::ORDER_SIMPLE_PRODUCT_COUNT_FROM ); - $quoteItemTableName = $this->getTableName( - 'quote_item', - \Magento\Quote\Model\ResourceModel\Quote\Item::class + $orderSimpleCountTo = (int)$this->fixtureModel->getValue( + 'order_simple_product_count_to', + self::ORDER_SIMPLE_PRODUCT_COUNT_TO ); - $quoteItemOptionTableName = $this->getTableName( - 'quote_item_option', - \Magento\Quote\Model\ResourceModel\Quote\Item\Option::class + $orderConfigurableCountFrom = (int)$this->fixtureModel->getValue( + 'order_configurable_product_count_from', + self::ORDER_CONFIGURABLE_PRODUCT_COUNT_FROM ); - $quotePaymentTableName = $this->getTableName( - 'quote_payment', - \Magento\Quote\Model\ResourceModel\Quote\Payment::class + $orderConfigurableCountTo = (int)$this->fixtureModel->getValue( + 'order_configurable_product_count_to', + self::ORDER_CONFIGURABLE_PRODUCT_COUNT_TO ); - $quoteAddressRateTableName = $this->getTableName( - 'quote_shipping_rate', - \Magento\Quote\Model\ResourceModel\Quote\Address\Rate::class + $orderBigConfigurableCountFrom = (int)$this->fixtureModel->getValue( + 'order_big_configurable_product_count_from', + self::ORDER_BIG_CONFIGURABLE_PRODUCT_COUNT_FROM ); - $reportEventTableName = $this->getTableName( - 'report_event', - \Magento\Reports\Model\ResourceModel\Event::class + $orderBigConfigurableCountTo = (int)$this->fixtureModel->getValue( + 'order_big_configurable_product_count_to', + self::ORDER_BIG_CONFIGURABLE_PRODUCT_COUNT_TO ); - $salesOrderTableName = $this->getTableName( + $this->orderQuotesEnable = (bool)$this->fixtureModel->getValue('order_quotes_enable', true); + + $entityId = $this->getMaxEntityId( 'sales_order', - \Magento\Sales\Model\ResourceModel\Order::class + \Magento\Sales\Model\ResourceModel\Order::class, + 'entity_id' ); - $salesOrderAddressTableName = $this->getTableName( - 'sales_order_address', - \Magento\Sales\Model\ResourceModel\Order::class - ); - $salesOrderGridTableName = $this->getTableName( - 'sales_order_grid', - \Magento\Sales\Model\ResourceModel\Order\Grid::class - ); - $salesOrderItemTableName = $this->getTableName( + $requestedOrders = (int)$this->fixtureModel->getValue('orders', 0); + if ($requestedOrders - $entityId < 1) { + return; + } + + $maxItemId = $this->getMaxEntityId( 'sales_order_item', - \Magento\Sales\Model\ResourceModel\Order\Item::class - ); - $salesOrderPaymentTableName = $this->getTableName( - 'sales_order_payment', - \Magento\Sales\Model\ResourceModel\Order\Payment::class + \Magento\Sales\Model\ResourceModel\Order\Item::class, + 'item_id' ); - $salesOrderStatusHistoryTableName = $this->getTableName( - 'sales_order_status_history', - \Magento\Sales\Model\ResourceModel\Order\Status\History::class - ); - $eavEntityStoreTableName = $this->getTableName( - 'eav_entity_store', - \Magento\Eav\Model\ResourceModel\Entity\Store::class - ); - /** @var \Magento\Store\Model\StoreManager $storeManager */ - $storeManager = $this->fixtureModel->getObjectManager()->create(\Magento\Store\Model\StoreManager::class); - /** @var $category \Magento\Catalog\Model\Category */ - $category = $this->fixtureModel->getObjectManager()->get(\Magento\Catalog\Model\Category::class); - /** @var $product \Magento\Catalog\Model\Product */ - $product = $this->fixtureModel->getObjectManager()->get(\Magento\Catalog\Model\Product::class); + $maxItemsPerOrder = $orderSimpleCountTo + ($orderConfigurableCountTo + $orderBigConfigurableCountTo) * 2; - $result = []; - $stores = $storeManager->getStores(); - foreach ($stores as $store) { - $storeId = $store->getStoreId(); - $websiteId = $store->getWebsite()->getId(); - $websiteName = $store->getWebsite()->getName(); - $groupName = $store->getGroup()->getName(); - $storeName = $store->getName(); - $storeRootCategory = $store->getRootCategoryId(); - $category->load($storeRootCategory); - $categoryResource = $category->getResource(); - //Get all categories - $resultsCategories = $categoryResource->getAllChildren($category); - foreach ($resultsCategories as $resultsCategory) { - $category->load($resultsCategory); - $structure = explode('/', $category->getPath()); - $pathSize = count($structure); - if ($pathSize > 1) { - $path = []; - for ($i = 1; $i < $pathSize; $i++) { - $path[] = $category->load($structure[$i])->getName(); - } - array_shift($path); - $resultsCategoryName = implode('/', $path); - } else { - $resultsCategoryName = $category->getName(); - } - //Not use root categories - if (trim($resultsCategoryName) != '') { - /** @var $productCategory \Magento\Catalog\Model\Category */ - $productCategory = $this->fixtureModel->getObjectManager() - ->get(\Magento\Catalog\Model\Category::class); - - /** @var $simpleProductCollection \Magento\Catalog\Model\ResourceModel\Product\Collection */ - $simpleProductCollection = $this->fixtureModel->getObjectManager()->create( - \Magento\Catalog\Model\ResourceModel\Product\Collection::class - ); - - $simpleProductCollection->addStoreFilter($storeId); - $simpleProductCollection->addWebsiteFilter($websiteId); - $simpleProductCollection->addCategoryFilter($productCategory->load($resultsCategory)); - $simpleProductCollection->getSelect()->where(" type_id = 'simple' "); - $simpleIds = $simpleProductCollection->getAllIds(2); - $simpleProductsResult = []; - foreach ($simpleIds as $key => $simpleId) { - $simpleProduct = $product->load($simpleId); - $simpleProductsResult[$key]['simpleProductId'] = $simpleId; - $simpleProductsResult[$key]['simpleProductSku'] = $simpleProduct->getSku(); - $simpleProductsResult[$key]['simpleProductName'] = $simpleProduct->getName(); - } + /** @var \Generator $itemIdSequence */ + $itemIdSequence = $this->getItemIdSequence($maxItemId, $requestedOrders, $maxItemsPerOrder); - $result[] = [ - $storeId, - $websiteName. '\n'. $groupName . '\n' . $storeName, - $simpleProductsResult - ]; - } + $this->prepareQueryTemplates(); + + $result = []; + foreach ($this->storeManager->getStores() as $store) { + $productsResult = []; + $this->storeManager->setCurrentStore($store->getId()); + + if ($orderSimpleCountTo > 0) { + $productsResult[Type::TYPE_SIMPLE] = $this->prepareSimpleProducts( + $this->getProductIds($store, Type::TYPE_SIMPLE, $orderSimpleCountTo) + ); + } + if ($orderConfigurableCountTo > 0) { + $productsResult[Configurable::TYPE_CODE] = $this->prepareConfigurableProducts( + $this->getProductIds($store, Configurable::TYPE_CODE, $orderConfigurableCountTo) + ); } + if ($orderBigConfigurableCountTo > 0) { + $productsResult[self::BIG_CONFIGURABLE_TYPE] = $this->prepareConfigurableProducts( + $this->getProductIds($store, self::BIG_CONFIGURABLE_TYPE, $orderBigConfigurableCountTo) + ); + } + + $result[] = [ + $store->getId(), + implode(PHP_EOL, [ + $this->storeManager->getWebsite($store->getWebsiteId())->getName(), + $this->storeManager->getGroup($store->getStoreGroupId())->getName(), + $store->getName() + ]), + $productsResult + ]; + } $productStoreId = function ($index) use ($result) { @@ -163,121 +256,427 @@ public function execute() $productStoreName = function ($index) use ($result) { return $result[$index % count($result)][1]; }; - - $simpleProductId[0] = function ($index) use ($result) { - return $result[$index % count($result)][2][0]['simpleProductId']; + $productId = function ($entityId, $index, $type) use ($result) { + return $result[$entityId % count($result)][2][$type][$index]['id']; }; - $simpleProductId[1] = function ($index) use ($result) { - return $result[$index % count($result)][2][1]['simpleProductId']; + $productSku = function ($entityId, $index, $type) use ($result) { + return $result[$entityId % count($result)][2][$type][$index]['sku']; }; - $simpleProductSku[0] = function ($index) use ($result) { - return $result[$index % count($result)][2][0]['simpleProductSku']; + $productName = function ($entityId, $index, $type) use ($result) { + return $result[$entityId % count($result)][2][$type][$index]['name']; }; - $simpleProductSku[1] = function ($index) use ($result) { - return $result[$index % count($result)][2][1]['simpleProductSku']; + $productBuyRequest = function ($entityId, $index, $type) use ($result) { + return $result[$entityId % count($result)][2][$type][$index]['buyRequest']; }; - $simpleProductName[0] = function ($index) use ($result) { - return $result[$index % count($result)][2][0]['simpleProductName']; + $productChildBuyRequest = function ($entityId, $index, $type) use ($result) { + return $result[$entityId % count($result)][2][$type][$index]['childBuyRequest']; }; - $simpleProductName[1] = function ($index) use ($result) { - return $result[$index % count($result)][2][1]['simpleProductName']; + $productChildId = function ($entityId, $index, $type) use ($result) { + return $result[$entityId % count($result)][2][$type][$index]['childId']; }; - $entityId = 1; - while ($entityId <= $ordersCount) { - $queries = []; - - $orderNumber = 100000000 * $productStoreId($entityId) + $entityId; - $email = 'order_' . $entityId . '@example.com'; - $firstName = 'First Name'; - $lastName = 'Last Name'; - $company = 'Company'; - $address = 'Address'; - $city = 'City'; - $state = 'Alabama'; - $country = 'US'; - $zip = '11111'; - $phone = '911'; - $dateStart = new \DateTime(); - $time = $dateStart->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT); - - $simpleProductIdLen[0] = strlen($simpleProductId[0]($entityId)); - $simpleProductIdLen[1] = strlen($simpleProductId[1]($entityId)); - - //@codingStandardsIgnoreStart - - $queries[] = "INSERT INTO `{$eavEntityStoreTableName}` (`entity_store_id`, `entity_type_id`, `store_id`, `increment_prefix`, `increment_last_id`) VALUES ({$productStoreId($entityId)}, 5, {$productStoreId($entityId)}, '{$productStoreId($entityId)}', '{$orderNumber}') ON DUPLICATE KEY UPDATE `increment_last_id`='{$orderNumber}';"; - - $quoteId = $entityId; - $queries[] = "INSERT INTO `{$quoteTableName}` (`entity_id`, `store_id`, `created_at`, `updated_at`, `converted_at`, `is_active`, `is_virtual`, `is_multi_shipping`, `items_count`, `items_qty`, `orig_order_id`, `store_to_base_rate`, `store_to_quote_rate`, `base_currency_code`, `store_currency_code`, `quote_currency_code`, `grand_total`, `base_grand_total`, `checkout_method`, `customer_id`, `customer_tax_class_id`, `customer_group_id`, `customer_email`, `customer_prefix`, `customer_firstname`, `customer_middlename`, `customer_lastname`, `customer_suffix`, `customer_dob`, `customer_note`, `customer_note_notify`, `customer_is_guest`, `remote_ip`, `applied_rule_ids`, `reserved_order_id`, `password_hash`, `coupon_code`, `global_currency_code`, `base_to_global_rate`, `base_to_quote_rate`, `customer_taxvat`, `customer_gender`, `subtotal`, `base_subtotal`, `subtotal_with_discount`, `base_subtotal_with_discount`, `is_changed`, `trigger_recollect`, `ext_shipping_info`, `is_persistent`, `gift_message_id`) VALUES ({$quoteId}, {$productStoreId($entityId)}, '{$time}', '1970-01-01 03:00:00', NULL, 0, 0, 0, 2, 2.0000, 0, 0.0000, 0.0000, 'USD', 'USD', 'USD', 25.3000, 25.3000, 'guest', NULL, 3, 0, '{$email}', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, 1, '127.0.0.1', '1', NULL, NULL, NULL, 'USD', 1.0000, 1.0000, NULL, NULL, 17.0000, 17.0000, 15.3000, 15.3000, 1, 0, NULL, 0, NULL);"; - - $quoteAddressId[0] = $entityId * 2 - 1; - $quoteAddressId[1] = $entityId * 2; - $queries[] = "INSERT INTO `{$quoteAddressTableName}` (`address_id`, `quote_id`, `created_at`, `updated_at`, `customer_id`, `save_in_address_book`, `customer_address_id`, `address_type`, `email`, `prefix`, `firstname`, `middlename`, `lastname`, `suffix`, `company`, `street`, `city`, `region`, `region_id`, `postcode`, `country_id`, `telephone`, `fax`, `same_as_billing`, `collect_shipping_rates`, `shipping_method`, `shipping_description`, `weight`, `subtotal`, `base_subtotal`, `subtotal_with_discount`, `base_subtotal_with_discount`, `tax_amount`, `base_tax_amount`, `shipping_amount`, `base_shipping_amount`, `shipping_tax_amount`, `base_shipping_tax_amount`, `discount_amount`, `base_discount_amount`, `grand_total`, `base_grand_total`, `customer_notes`, `applied_taxes`, `discount_description`, `shipping_discount_amount`, `base_shipping_discount_amount`, `subtotal_incl_tax`, `base_subtotal_total_incl_tax`, `discount_tax_compensation_amount`, `base_discount_tax_compensation_amount`, `shipping_discount_tax_compensation_amount`, `base_shipping_discount_tax_compensation_amnt`, `shipping_incl_tax`, `base_shipping_incl_tax`, `free_shipping`, `vat_id`, `vat_is_valid`, `vat_request_id`, `vat_request_date`, `vat_request_success`, `gift_message_id`) VALUES ({$quoteAddressId[0]}, {$quoteId}, '{$time}', '1970-01-01 03:00:00', NULL, 1, NULL, 'billing', '{$email}', NULL, '{$firstName}', NULL, '{$lastName}', NULL, '{$company}', '{$address}', '{$city}', '{$state}', 1, '{$zip}', '{$country}', '{$phone}', NULL, 0, 0, NULL, NULL, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, 0.0000, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, 0.0000, NULL, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);"; - $queries[] = "INSERT INTO `{$quoteAddressTableName}` (`address_id`, `quote_id`, `created_at`, `updated_at`, `customer_id`, `save_in_address_book`, `customer_address_id`, `address_type`, `email`, `prefix`, `firstname`, `middlename`, `lastname`, `suffix`, `company`, `street`, `city`, `region`, `region_id`, `postcode`, `country_id`, `telephone`, `fax`, `same_as_billing`, `collect_shipping_rates`, `shipping_method`, `shipping_description`, `weight`, `subtotal`, `base_subtotal`, `subtotal_with_discount`, `base_subtotal_with_discount`, `tax_amount`, `base_tax_amount`, `shipping_amount`, `base_shipping_amount`, `shipping_tax_amount`, `base_shipping_tax_amount`, `discount_amount`, `base_discount_amount`, `grand_total`, `base_grand_total`, `customer_notes`, `applied_taxes`, `discount_description`, `shipping_discount_amount`, `base_shipping_discount_amount`, `subtotal_incl_tax`, `base_subtotal_total_incl_tax`, `discount_tax_compensation_amount`, `base_discount_tax_compensation_amount`, `shipping_discount_tax_compensation_amount`, `base_shipping_discount_tax_compensation_amnt`, `shipping_incl_tax`, `base_shipping_incl_tax`, `free_shipping`, `vat_id`, `vat_is_valid`, `vat_request_id`, `vat_request_date`, `vat_request_success`, `gift_message_id`) VALUES ({$quoteAddressId[1]}, {$quoteId}, '{$time}', '1970-01-01 03:00:00', NULL, 0, NULL, 'shipping', '{$email}', NULL, '{$firstName}', NULL, '{$lastName}', NULL, '{$company}', '{$address}', '{$city}', '{$state}', 1, '{$zip}', '{$country}', '{$phone}', NULL, 1, 0, 'flatrate_flatrate', 'Flat Rate - Fixed', 2.0000, 17.0000, 17.0000, 0.0000, 0.0000, 0.0000, 0.0000, 10.0000, 10.0000, 0.0000, 0.0000, -1.7000, -1.7000, 25.3000, 25.3000, NULL, 'a:0:{}', NULL, 0.0000, 0.0000, 17.0000, NULL, 0.0000, 0.0000, 0.0000, NULL, 10.0000, 10.0000, 0, NULL, NULL, NULL, NULL, NULL, NULL);"; - - $quoteItemId[0] = $entityId * 4 - 3; - $quoteItemId[1] = $entityId * 4 - 2; - $quoteItemId[2] = $entityId * 4 - 1; - $quoteItemId[3] = $entityId * 4; - $queries[] = "INSERT INTO `{$quoteItemTableName}` (`item_id`, `quote_id`, `created_at`, `updated_at`, `product_id`, `store_id`, `parent_item_id`, `is_virtual`, `sku`, `name`, `description`, `applied_rule_ids`, `additional_data`, `is_qty_decimal`, `no_discount`, `weight`, `qty`, `price`, `base_price`, `custom_price`, `discount_percent`, `discount_amount`, `base_discount_amount`, `tax_percent`, `tax_amount`, `base_tax_amount`, `row_total`, `base_row_total`, `row_total_with_discount`, `row_weight`, `product_type`, `base_tax_before_discount`, `tax_before_discount`, `original_custom_price`, `redirect_url`, `base_cost`, `price_incl_tax`, `base_price_incl_tax`, `row_total_incl_tax`, `base_row_total_incl_tax`, `discount_tax_compensation_amount`, `base_discount_tax_compensation_amount`, `free_shipping`, `gift_message_id`, `weee_tax_applied`, `weee_tax_applied_amount`, `weee_tax_applied_row_amount`, `weee_tax_disposition`, `weee_tax_row_disposition`, `base_weee_tax_applied_amount`, `base_weee_tax_applied_row_amnt`, `base_weee_tax_disposition`, `base_weee_tax_row_disposition`) VALUES ({$quoteItemId[0]}, {$quoteId}, '1970-01-01 03:00:00', '1970-01-01 03:00:00', {$simpleProductId[0]($entityId)}, {$productStoreId($entityId)}, NULL, 0, '{$simpleProductSku[0]($entityId)}', '{$simpleProductName[0]($entityId)}', NULL, '1', NULL, 0, 0, 1.0000, 1.0000, 8.5000, 8.5000, NULL, 10.0000, 0.8500, 0.8500, 0.0000, 0.0000, 0.0000, 8.5000, 8.5000, 0.0000, 1.0000, 'simple', NULL, NULL, NULL, NULL, NULL, 8.5000, 8.5000, 8.5000, 8.5000, 0.0000, 0.0000, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);"; - $queries[] = "INSERT INTO `{$quoteItemTableName}` (`item_id`, `quote_id`, `created_at`, `updated_at`, `product_id`, `store_id`, `parent_item_id`, `is_virtual`, `sku`, `name`, `description`, `applied_rule_ids`, `additional_data`, `is_qty_decimal`, `no_discount`, `weight`, `qty`, `price`, `base_price`, `custom_price`, `discount_percent`, `discount_amount`, `base_discount_amount`, `tax_percent`, `tax_amount`, `base_tax_amount`, `row_total`, `base_row_total`, `row_total_with_discount`, `row_weight`, `product_type`, `base_tax_before_discount`, `tax_before_discount`, `original_custom_price`, `redirect_url`, `base_cost`, `price_incl_tax`, `base_price_incl_tax`, `row_total_incl_tax`, `base_row_total_incl_tax`, `discount_tax_compensation_amount`, `base_discount_tax_compensation_amount`, `free_shipping`, `gift_message_id`, `weee_tax_applied`, `weee_tax_applied_amount`, `weee_tax_applied_row_amount`, `weee_tax_disposition`, `weee_tax_row_disposition`, `base_weee_tax_applied_amount`, `base_weee_tax_applied_row_amnt`, `base_weee_tax_disposition`, `base_weee_tax_row_disposition`) VALUES ({$quoteItemId[1]}, {$quoteId}, '1970-01-01 03:00:00', '1970-01-01 03:00:00', {$simpleProductId[1]($entityId)}, {$productStoreId($entityId)}, NULL, 0, '{$simpleProductSku[1]($entityId)}', '{$simpleProductName[1]($entityId)}', NULL, '1', NULL, 0, 0, 1.0000, 1.0000, 8.5000, 8.5000, NULL, 10.0000, 0.8500, 0.8500, 0.0000, 0.0000, 0.0000, 8.5000, 8.5000, 0.0000, 1.0000, 'simple', NULL, NULL, NULL, NULL, NULL, 8.5000, 8.5000, 8.5000, 8.5000, 0.0000, 0.0000, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);"; - - $quoteItemOptionId[0] = $entityId * 8 - 7; - $quoteItemOptionId[1] = $entityId * 8 - 6; - $quoteItemOptionId[2] = $entityId * 8 - 5; - $quoteItemOptionId[3] = $entityId * 8 - 4; - $quoteItemOptionId[4] = $entityId * 8 - 3; - $quoteItemOptionId[5] = $entityId * 8 - 2; - $quoteItemOptionId[6] = $entityId * 8 - 1; - $quoteItemOptionId[7] = $entityId * 8; - $queries[] = "INSERT INTO `{$quoteItemOptionTableName}` (`option_id`, `item_id`, `product_id`, `code`, `value`) VALUES ({$quoteItemOptionId[0]}, {$quoteItemId[0]}, {$simpleProductId[0]($entityId)}, 'info_buyRequest', 'a:3:{s:4:\"uenc\";s:44:\"aHR0cDovL21hZ2UyLmNvbS9jYXRlZ29yeS0xLmh0bWw,\";s:7:\"product\";s:{$simpleProductIdLen[0]}:\"{$simpleProductId[0]($entityId)}\";s:3:\"qty\";i:1;}');"; - $queries[] = "INSERT INTO `{$quoteItemOptionTableName}` (`option_id`, `item_id`, `product_id`, `code`, `value`) VALUES ({$quoteItemOptionId[1]}, {$quoteItemId[1]}, {$simpleProductId[1]($entityId)}, 'info_buyRequest', 'a:3:{s:4:\"uenc\";s:44:\"aHR0cDovL21hZ2UyLmNvbS9jYXRlZ29yeS0xLmh0bWw,\";s:7:\"product\";s:{$simpleProductIdLen[1]}:\"{$simpleProductId[1]($entityId)}\";s:3:\"qty\";i:1;}');"; - - $quotePaymentId = $quoteId; - $queries[] = "INSERT INTO `{$quotePaymentTableName}` (`payment_id`, `quote_id`, `created_at`, `updated_at`, `method`, `cc_type`, `cc_number_enc`, `cc_last_4`, `cc_cid_enc`, `cc_owner`, `cc_exp_month`, `cc_exp_year`, `cc_ss_owner`, `cc_ss_start_month`, `cc_ss_start_year`, `po_number`, `additional_data`, `cc_ss_issue`, `additional_information`) VALUES ({$quotePaymentId}, {$quoteId}, '{$time}', '1970-01-01 03:00:00', 'checkmo', NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, NULL, NULL, NULL, NULL);"; - - $quoteShippingRateId = $quoteAddressId[1]; - $queries[] = "INSERT INTO `{$quoteAddressRateTableName}` (`rate_id`, `address_id`, `created_at`, `updated_at`, `carrier`, `carrier_title`, `code`, `method`, `method_description`, `price`, `error_message`, `method_title`) VALUES ({$quoteShippingRateId}, {$quoteAddressId[1]}, '{$time}', '1970-01-01 03:00:00', 'flatrate', 'Flat Rate', 'flatrate_flatrate', 'flatrate', NULL, 10.0000, NULL, 'Fixed');"; - - $reportEventId[0] = $quoteItemId[0]; - $reportEventId[1] = $quoteItemId[1]; - $reportEventId[2] = $quoteItemId[2]; - $reportEventId[3] = $quoteItemId[3]; - $queries[] = "INSERT INTO `{$reportEventTableName}` (`event_id`, `logged_at`, `event_type_id`, `object_id`, `subject_id`, `subtype`, `store_id`) VALUES ({$reportEventId[0]}, '{$time}', 4, {$simpleProductId[0]($entityId)}, 2, 1, {$productStoreId($entityId)});"; - $queries[] = "INSERT INTO `{$reportEventTableName}` (`event_id`, `logged_at`, `event_type_id`, `object_id`, `subject_id`, `subtype`, `store_id`) VALUES ({$reportEventId[1]}, '{$time}', 4, {$simpleProductId[1]($entityId)}, 2, 1, {$productStoreId($entityId)});"; - - $salesOrderId = $quoteId; - $queries[] = "INSERT INTO `{$salesOrderTableName}` (`entity_id`, `state`, `status`, `coupon_code`, `protect_code`, `shipping_description`, `is_virtual`, `store_id`, `customer_id`, `base_discount_amount`, `base_discount_canceled`, `base_discount_invoiced`, `base_discount_refunded`, `base_grand_total`, `base_shipping_amount`, `base_shipping_canceled`, `base_shipping_invoiced`, `base_shipping_refunded`, `base_shipping_tax_amount`, `base_shipping_tax_refunded`, `base_subtotal`, `base_subtotal_canceled`, `base_subtotal_invoiced`, `base_subtotal_refunded`, `base_tax_amount`, `base_tax_canceled`, `base_tax_invoiced`, `base_tax_refunded`, `base_to_global_rate`, `base_to_order_rate`, `base_total_canceled`, `base_total_invoiced`, `base_total_invoiced_cost`, `base_total_offline_refunded`, `base_total_online_refunded`, `base_total_paid`, `base_total_qty_ordered`, `base_total_refunded`, `discount_amount`, `discount_canceled`, `discount_invoiced`, `discount_refunded`, `grand_total`, `shipping_amount`, `shipping_canceled`, `shipping_invoiced`, `shipping_refunded`, `shipping_tax_amount`, `shipping_tax_refunded`, `store_to_base_rate`, `store_to_order_rate`, `subtotal`, `subtotal_canceled`, `subtotal_invoiced`, `subtotal_refunded`, `tax_amount`, `tax_canceled`, `tax_invoiced`, `tax_refunded`, `total_canceled`, `total_invoiced`, `total_offline_refunded`, `total_online_refunded`, `total_paid`, `total_qty_ordered`, `total_refunded`, `can_ship_partially`, `can_ship_partially_item`, `customer_is_guest`, `customer_note_notify`, `billing_address_id`, `customer_group_id`, `edit_increment`, `email_sent`, `send_email`, `forced_shipment_with_invoice`, `payment_auth_expiration`, `quote_address_id`, `quote_id`, `shipping_address_id`, `adjustment_negative`, `adjustment_positive`, `base_adjustment_negative`, `base_adjustment_positive`, `base_shipping_discount_amount`, `base_subtotal_incl_tax`, `base_total_due`, `payment_authorization_amount`, `shipping_discount_amount`, `subtotal_incl_tax`, `total_due`, `weight`, `customer_dob`, `increment_id`, `applied_rule_ids`, `base_currency_code`, `customer_email`, `customer_firstname`, `customer_lastname`, `customer_middlename`, `customer_prefix`, `customer_suffix`, `customer_taxvat`, `discount_description`, `ext_customer_id`, `ext_order_id`, `global_currency_code`, `hold_before_state`, `hold_before_status`, `order_currency_code`, `original_increment_id`, `relation_child_id`, `relation_child_real_id`, `relation_parent_id`, `relation_parent_real_id`, `remote_ip`, `shipping_method`, `store_currency_code`, `store_name`, `x_forwarded_for`, `customer_note`, `created_at`, `updated_at`, `total_item_count`, `customer_gender`, `discount_tax_compensation_amount`, `base_discount_tax_compensation_amount`, `shipping_discount_tax_compensation_amount`, `base_shipping_discount_tax_compensation_amnt`, `discount_tax_compensation_invoiced`, `base_discount_tax_compensation_invoiced`, `discount_tax_compensation_refunded`, `base_discount_tax_compensation_refunded`, `shipping_incl_tax`, `base_shipping_incl_tax`, `coupon_rule_name`, `gift_message_id`) VALUES ({$salesOrderId}, 'new', 'pending', NULL, '272ecb', 'Flat Rate - Fixed', 0, {$productStoreId($entityId)}, NULL, -1.7000, NULL, NULL, NULL, 25.3000, 10.0000, NULL, NULL, NULL, 0.0000, NULL, 17.0000, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, 1.0000, 1.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -1.7000, NULL, NULL, NULL, 25.3000, 10.0000, NULL, NULL, NULL, 0.0000, NULL, 0.0000, 0.0000, 17.0000, NULL, NULL, NULL, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2.0000, NULL, NULL, NULL, 1, 1, 2, 0, NULL, 1, 1, NULL, NULL, NULL, 1, 1, NULL, NULL, NULL, NULL, NULL, 17.0000, 25.3000, NULL, NULL, 17.0000, 25.3000, 2.0000, NULL, {$orderNumber}, '1', 'USD', '{$email}', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'USD', NULL, NULL, 'USD', NULL, NULL, NULL, NULL, NULL, '127.0.0.1', 'flatrate_flatrate', 'USD', '{$productStoreName($entityId)}', NULL, NULL, '{$time}', '{$time}', 2, NULL, 0.0000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, 10.0000, 10.0000, NULL, NULL);"; - - $salesOrderAddressId[0] = $quoteAddressId[0]; - $salesOrderAddressId[1] = $quoteAddressId[1]; - $queries[] = "INSERT INTO `{$salesOrderAddressTableName}` (`entity_id`, `parent_id`, `customer_address_id`, `quote_address_id`, `region_id`, `customer_id`, `fax`, `region`, `postcode`, `lastname`, `street`, `city`, `email`, `telephone`, `country_id`, `firstname`, `address_type`, `prefix`, `middlename`, `suffix`, `company`, `vat_id`, `vat_is_valid`, `vat_request_id`, `vat_request_date`, `vat_request_success`) VALUES ({$salesOrderAddressId[0]}, {$salesOrderId}, NULL, NULL, 1, NULL, NULL, '{$state}', '{$zip}', '{$lastName}', '{$address}', '{$city}', '{$email}', '{$phone}', '{$country}', '{$firstName}', 'shipping', NULL, NULL, NULL, '{$company}', NULL, NULL, NULL, NULL, NULL);"; - $queries[] = "INSERT INTO `{$salesOrderAddressTableName}` (`entity_id`, `parent_id`, `customer_address_id`, `quote_address_id`, `region_id`, `customer_id`, `fax`, `region`, `postcode`, `lastname`, `street`, `city`, `email`, `telephone`, `country_id`, `firstname`, `address_type`, `prefix`, `middlename`, `suffix`, `company`, `vat_id`, `vat_is_valid`, `vat_request_id`, `vat_request_date`, `vat_request_success`) VALUES ({$salesOrderAddressId[1]}, {$salesOrderId}, NULL, NULL, 1, NULL, NULL, '{$state}', '{$zip}', '{$lastName}', '{$address}', '{$city}', '{$email}', '{$phone}', '{$country}', '{$firstName}', 'billing', NULL, NULL, NULL, '{$company}', NULL, NULL, NULL, NULL, NULL);"; - - $salesOrderGridId = $salesOrderId; - $queries[] = "INSERT INTO `{$salesOrderGridTableName}` (`entity_id`, `status`, `store_id`, `store_name`, `customer_id`, `base_grand_total`, `base_total_paid`, `grand_total`, `total_paid`, `increment_id`, `base_currency_code`, `order_currency_code`, `shipping_name`, `billing_name`, `created_at`, `updated_at`) VALUES ({$salesOrderGridId}, 'pending', {$productStoreId($entityId)}, '{$productStoreName($entityId)}', NULL, 25.3000, NULL, 25.3000, NULL, {$orderNumber}, 'USD', 'USD', '', '', '{$time}', NULL);"; - - $salesOrderItemId[0] = $quoteItemId[0]; - $salesOrderItemId[1] = $quoteItemId[1]; - $salesOrderItemId[2] = $quoteItemId[2]; - $salesOrderItemId[3] = $quoteItemId[3]; - $queries[] = "INSERT INTO `{$salesOrderItemTableName}` (`item_id`, `order_id`, `parent_item_id`, `quote_item_id`, `store_id`, `created_at`, `updated_at`, `product_id`, `product_type`, `product_options`, `weight`, `is_virtual`, `sku`, `name`, `description`, `applied_rule_ids`, `additional_data`, `is_qty_decimal`, `no_discount`, `qty_backordered`, `qty_canceled`, `qty_invoiced`, `qty_ordered`, `qty_refunded`, `qty_shipped`, `base_cost`, `price`, `base_price`, `original_price`, `base_original_price`, `tax_percent`, `tax_amount`, `base_tax_amount`, `tax_invoiced`, `base_tax_invoiced`, `discount_percent`, `discount_amount`, `base_discount_amount`, `discount_invoiced`, `base_discount_invoiced`, `amount_refunded`, `base_amount_refunded`, `row_total`, `base_row_total`, `row_invoiced`, `base_row_invoiced`, `row_weight`, `base_tax_before_discount`, `tax_before_discount`, `ext_order_item_id`, `locked_do_invoice`, `locked_do_ship`, `price_incl_tax`, `base_price_incl_tax`, `row_total_incl_tax`, `base_row_total_incl_tax`, `discount_tax_compensation_amount`, `base_discount_tax_compensation_amount`, `discount_tax_compensation_invoiced`, `base_discount_tax_compensation_invoiced`, `discount_tax_compensation_refunded`, `base_discount_tax_compensation_refunded`, `tax_canceled`, `discount_tax_compensation_canceled`, `tax_refunded`, `base_tax_refunded`, `discount_refunded`, `base_discount_refunded`, `free_shipping`, `gift_message_id`, `gift_message_available`, `weee_tax_applied`, `weee_tax_applied_amount`, `weee_tax_applied_row_amount`, `weee_tax_disposition`, `weee_tax_row_disposition`, `base_weee_tax_applied_amount`, `base_weee_tax_applied_row_amnt`, `base_weee_tax_disposition`, `base_weee_tax_row_disposition`) VALUES ({$salesOrderItemId[0]}, {$salesOrderId}, NULL, {$quoteItemId[0]}, {$productStoreId($entityId)}, '{$time}', '0000-00-00 00:00:00', {$simpleProductId[0]($entityId)}, 'simple', 'a:1:{s:15:\"info_buyRequest\";a:3:{s:4:\"uenc\";s:44:\"aHR0cDovL21hZ2UyLmNvbS9jYXRlZ29yeS0xLmh0bWw,\";s:7:\"product\";s:{$simpleProductIdLen[0]}:\"{$simpleProductId[0]($entityId)}\";s:3:\"qty\";i:1;}}', 1.0000, 0, '{$simpleProductSku[0]($entityId)}', '{$simpleProductName[0]($entityId)}', NULL, '1', NULL, 0, 0, NULL, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, NULL, 8.5000, 8.5000, 10.0000, 10.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 10.0000, 0.8500, 0.8500, 0.0000, 0.0000, 0.0000, 0.0000, 8.5000, 8.5000, 0.0000, 0.0000, 1.0000, NULL, NULL, NULL, NULL, NULL, 8.5000, 8.5000, 8.5000, 8.5000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);"; - $queries[] = "INSERT INTO `{$salesOrderItemTableName}` (`item_id`, `order_id`, `parent_item_id`, `quote_item_id`, `store_id`, `created_at`, `updated_at`, `product_id`, `product_type`, `product_options`, `weight`, `is_virtual`, `sku`, `name`, `description`, `applied_rule_ids`, `additional_data`, `is_qty_decimal`, `no_discount`, `qty_backordered`, `qty_canceled`, `qty_invoiced`, `qty_ordered`, `qty_refunded`, `qty_shipped`, `base_cost`, `price`, `base_price`, `original_price`, `base_original_price`, `tax_percent`, `tax_amount`, `base_tax_amount`, `tax_invoiced`, `base_tax_invoiced`, `discount_percent`, `discount_amount`, `base_discount_amount`, `discount_invoiced`, `base_discount_invoiced`, `amount_refunded`, `base_amount_refunded`, `row_total`, `base_row_total`, `row_invoiced`, `base_row_invoiced`, `row_weight`, `base_tax_before_discount`, `tax_before_discount`, `ext_order_item_id`, `locked_do_invoice`, `locked_do_ship`, `price_incl_tax`, `base_price_incl_tax`, `row_total_incl_tax`, `base_row_total_incl_tax`, `discount_tax_compensation_amount`, `base_discount_tax_compensation_amount`, `discount_tax_compensation_invoiced`, `base_discount_tax_compensation_invoiced`, `discount_tax_compensation_refunded`, `base_discount_tax_compensation_refunded`, `tax_canceled`, `discount_tax_compensation_canceled`, `tax_refunded`, `base_tax_refunded`, `discount_refunded`, `base_discount_refunded`, `free_shipping`, `gift_message_id`, `gift_message_available`, `weee_tax_applied`, `weee_tax_applied_amount`, `weee_tax_applied_row_amount`, `weee_tax_disposition`, `weee_tax_row_disposition`, `base_weee_tax_applied_amount`, `base_weee_tax_applied_row_amnt`, `base_weee_tax_disposition`, `base_weee_tax_row_disposition`) VALUES ({$salesOrderItemId[1]}, {$salesOrderId}, NULL, {$quoteItemId[1]}, {$productStoreId($entityId)}, '{$time}', '0000-00-00 00:00:00', {$simpleProductId[1]($entityId)}, 'simple', 'a:1:{s:15:\"info_buyRequest\";a:3:{s:4:\"uenc\";s:44:\"aHR0cDovL21hZ2UyLmNvbS9jYXRlZ29yeS0xLmh0bWw,\";s:7:\"product\";s:{$simpleProductIdLen[1]}:\"{$simpleProductId[1]($entityId)}\";s:3:\"qty\";i:1;}}', 1.0000, 0, '{$simpleProductSku[1]($entityId)}', '{$simpleProductName[1]($entityId)}', NULL, '1', NULL, 0, 0, NULL, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, NULL, 8.5000, 8.5000, 10.0000, 10.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 10.0000, 0.8500, 0.8500, 0.0000, 0.0000, 0.0000, 0.0000, 8.5000, 8.5000, 0.0000, 0.0000, 1.0000, NULL, NULL, NULL, NULL, NULL, 8.5000, 8.5000, 8.5000, 8.5000, 0.0000, 0.0000, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);"; - - $salesOrderPaymentId = $salesOrderId; - $queries[] = "INSERT INTO `{$salesOrderPaymentTableName}` (`entity_id`, `parent_id`, `base_shipping_captured`, `shipping_captured`, `amount_refunded`, `base_amount_paid`, `amount_canceled`, `base_amount_authorized`, `base_amount_paid_online`, `base_amount_refunded_online`, `base_shipping_amount`, `shipping_amount`, `amount_paid`, `amount_authorized`, `base_amount_ordered`, `base_shipping_refunded`, `shipping_refunded`, `base_amount_refunded`, `amount_ordered`, `base_amount_canceled`, `quote_payment_id`, `additional_data`, `cc_exp_month`, `cc_ss_start_year`, `echeck_bank_name`, `method`, `cc_debug_request_body`, `cc_secure_verify`, `protection_eligibility`, `cc_approval`, `cc_last_4`, `cc_status_description`, `echeck_type`, `cc_debug_response_serialized`, `cc_ss_start_month`, `echeck_account_type`, `last_trans_id`, `cc_cid_status`, `cc_owner`, `cc_type`, `po_number`, `cc_exp_year`, `cc_status`, `echeck_routing_number`, `account_status`, `anet_trans_method`, `cc_debug_response_body`, `cc_ss_issue`, `echeck_account_name`, `cc_avs_status`, `cc_number_enc`, `cc_trans_id`, `address_status`, `additional_information`) VALUES ({$salesOrderPaymentId}, {$salesOrderId}, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 10.0000, 10.0000, NULL, NULL, 25.3000, NULL, NULL, NULL, 25.3000, NULL, NULL, NULL, NULL, '0', NULL, 'checkmo', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '0', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'a:1:{s:53:\"a:1:{s:12:\"method_title\";s:19:\"Check / Money order\";}\";N;}');"; - - $salesOrderStatusHistoryId = $salesOrderId; - $queries[] = "INSERT INTO `{$salesOrderStatusHistoryTableName}` (`entity_id`, `parent_id`, `is_customer_notified`, `is_visible_on_front`, `comment`, `status`, `created_at`, `entity_name`) VALUES ({$salesOrderStatusHistoryId}, {$salesOrderId}, 1, 0, NULL, 'pending', '{$time}', 'order');"; - - // @codingStandardsIgnoreEnd - foreach ($queries as $query) { - $connection->query($query); + $address = [ + '%firstName%' => 'First Name', + '%lastName%' => 'Last Name', + '%company%' => 'Company', + '%address%' => 'Address', + '%city%' => 'city', + '%state%' => 'Alabama', + '%country%' => 'US', + '%zip%' => '11111', + '%phone%' => '911' + ]; + + $batchNumber = 0; + $entityId++; + while ($entityId <= $requestedOrders) { + $batchNumber++; + $productCount = [ + Type::TYPE_SIMPLE => mt_rand($orderSimpleCountFrom, $orderSimpleCountTo), + Configurable::TYPE_CODE => mt_rand($orderConfigurableCountFrom, $orderConfigurableCountTo), + self::BIG_CONFIGURABLE_TYPE => mt_rand($orderBigConfigurableCountFrom, $orderBigConfigurableCountTo) + ]; + $order = [ + '%itemsPerOrder%' => array_sum($productCount), + '%orderNumber%' => 100000000 * $productStoreId($entityId) + $entityId, + '%email%' => "order_{$entityId}@example.com", + '%time%' => date(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT), + '%productStoreId%' => $productStoreId($entityId), + '%productStoreName%' => $productStoreName($entityId), + '%entityId%' => $entityId, + ]; + $shippingAddress = ['%orderAddressId%' => $entityId * 2 - 1, '%addressType%' => 'shipping']; + $billingAddress = ['%orderAddressId%' => $entityId * 2, '%addressType%' => 'billing']; + + try { + $this->query('quote', $order); + $this->query('quote_address', $order, $address, $shippingAddress); + $this->query('quote_address', $order, $address, $billingAddress); + $this->query('quote_payment', $order); + $this->query('quote_shipping_rate', $order, $shippingAddress); + + $this->query('eav_entity_store', $order); + $this->query('sales_order', $order); + $this->query('sales_order_address', $order, $address, $shippingAddress); + $this->query('sales_order_address', $order, $address, $billingAddress); + $this->query('sales_order_grid', $order); + $this->query('sales_order_payment', $order); + $this->query('sales_order_status_history', $order); + + for ($i = 0; $i < $productCount[Type::TYPE_SIMPLE]; $i++) { + $itemData = [ + '%productId%' => $productId($entityId, $i, Type::TYPE_SIMPLE), + '%sku%' => $productSku($entityId, $i, Type::TYPE_SIMPLE), + '%name%' => $productName($entityId, $i, Type::TYPE_SIMPLE), + '%itemId%' => $itemIdSequence->current(), + '%productType%' => Type::TYPE_SIMPLE, + '%productOptions%' => $productBuyRequest($entityId, $i, Type::TYPE_SIMPLE), + '%parentItemId%' => 'null', + ]; + $this->query('sales_order_item', $order, $itemData); + $this->query('quote_item', $order, $itemData); + $this->query('quote_item_option', $order, $itemData, [ + '%code%' => 'info_buyRequest', + '%value%' => $this->serializer->serialize([ + 'product' => $productId($entityId, $i, Type::TYPE_SIMPLE), + 'qty' => "1", + 'uenc' => 'aHR0cDovL21hZ2UyLmNvbS9jYXRlZ29yeS0xLmh0bWw' + ]) + ]); + $itemIdSequence->next(); + } + + foreach ([Configurable::TYPE_CODE, self::BIG_CONFIGURABLE_TYPE] as $type) { + for ($i = 0; $i < $productCount[$type]; $i++) { + // Generate parent item + $parentItemId = $itemIdSequence->current(); + $itemData = [ + '%productId%' => $productId($entityId, $i, $type), + '%sku%' => $productSku($entityId, $i, $type), + '%name%' => $productName($entityId, $i, $type), + '%productOptions%' => $productBuyRequest($entityId, $i, $type)['order'], + '%itemId%' => $parentItemId, + '%parentItemId%' => 'null', + '%productType%' => Configurable::TYPE_CODE + ]; + $this->query('sales_order_item', $order, $itemData); + $this->query('quote_item', $order, $itemData); + $this->query('quote_item_option', $order, $itemData, [ + '%code%' => 'info_buyRequest', + '%value%' => $productBuyRequest($entityId, $i, $type)['quote'] + ]); + $this->query('quote_item_option', $order, $itemData, [ + '%code%' => 'attributes', + '%value%' => $productBuyRequest($entityId, $i, $type)['super_attribute'] + ]); + $itemData['%productId%'] = $productChildId($entityId, $i, $type); + $this->query('quote_item_option', $itemData, [ + '%code%' => "product_qty_" . $productChildId($entityId, $i, $type), + '%value%' => "1" + ]); + $this->query('quote_item_option', $itemData, [ + '%code%' => "simple_product", + '%value%' => $productChildId($entityId, $i, $type) + ]); + $itemIdSequence->next(); + + // Generate child item + $itemData = [ + '%productId%' => $productChildId($entityId, $i, $type), + '%sku%' => $productSku($entityId, $i, $type), + '%name%' => $productName($entityId, $i, $type), + '%productOptions%' => $productChildBuyRequest($entityId, $i, $type)['order'], + '%itemId%' => $itemIdSequence->current(), + '%parentItemId%' => $parentItemId, + '%productType%' => Type::TYPE_SIMPLE + ]; + + $this->query('sales_order_item', $order, $itemData); + $this->query('quote_item', $order, $itemData); + $this->query('quote_item_option', $itemData, [ + '%code%' => "info_buyRequest", + '%value%' => $productChildBuyRequest($entityId, $i, $type)['quote'] + ]); + $this->query('quote_item_option', $itemData, [ + '%code%' => "parent_product_id", + '%value%' => $productId($entityId, $i, $type) + ]); + $itemIdSequence->next(); + } + } + } catch (\Exception $lastException) { + foreach ($this->resourceConnections as $connection) { + if ($connection->getTransactionLevel() > 0) { + $connection->rollBack(); + } + } + throw $lastException; } + if ($batchNumber >= self::BATCH_SIZE) { + $this->commitBatch(); + $batchNumber = 0; + } $entityId++; } + + foreach ($this->resourceConnections as $connection) { + if ($connection->getTransactionLevel() > 0) { + $connection->commit(); + } + } + } + + /** + * Load and prepare INSERT query templates data from external file. + * + * Queries are prepared using external json file, where keys are DB column names and values represent data, + * to be inserted to the table. Data may contain a default value or a placeholder which is replaced later during + * flow (in the query method of this class). + * Additionally, in case if multiple DB connections are set up, transaction is started for each connection. + * + * @return void + */ + private function prepareQueryTemplates() + { + $fileName = __DIR__ . DIRECTORY_SEPARATOR . "_files" . DIRECTORY_SEPARATOR . "orders_fixture_data.json"; + $templateData = json_decode(file_get_contents(realpath($fileName)), true); + foreach ($templateData as $table => $template) { + if (isset($template['_table'])) { + $table = $template['_table']; + unset($template['_table']); + } + if (isset($template['_resource'])) { + $resource = $template['_resource']; + unset($template['_resource']); + } else { + $resource = explode("_", $table); + foreach ($resource as &$item) { + $item = ucfirst($item); + } + $resource = "Magento\\" + . array_shift($resource) + . "\\Model\\ResourceModel\\" + . implode("\\", $resource); + } + + $tableName = $this->getTableName($table, $resource); + + $querySuffix = ""; + if (isset($template['_query_suffix'])) { + $querySuffix = $template['_query_suffix']; + unset($template['_query_suffix']); + } + + $fields = implode(', ', array_keys($template)); + $values = implode(', ', array_values($template)); + + /** @var \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resourceModel */ + $resourceModel = $this->fixtureModel->getObjectManager()->get($resource); + $connection = $resourceModel->getConnection(); + if ($connection->getTransactionLevel() == 0) { + $connection->beginTransaction(); + } + + $this->queryTemplates[$table] = "INSERT INTO `{$tableName}` ({$fields}) VALUES ({$values}){$querySuffix};"; + $this->resourceConnections[$table] = $connection; + } + } + + /** + * Build and execute query. + * + * Builds a database query by replacing placeholder values in the cached queries and executes query in appropriate + * DB connection (if setup). Additionally filters out quote-related queries, if appropriate flag is set. + * + * @param string $table + * @param array ...$replacements + * @return void + */ + protected function query($table, ... $replacements) + { + if (!$this->orderQuotesEnable && strpos($table, "quote") !== false) { + return; + } + $query = $this->queryTemplates[$table]; + foreach ($replacements as $data) { + $query = str_replace(array_keys($data), array_values($data), $query); + } + + $this->resourceConnections[$table]->query($query); + } + + /** + * Get maximum order id currently existing in the database. + * + * To support incremental generation of the orders it is necessary to get the maximum order entity_id currently + * existing in the database. + * + * @param string $tableName + * @param string $resourceName + * @param string $column + * @return int + */ + private function getMaxEntityId($tableName, $resourceName, $column = 'entity_id') + { + $tableName = $this->getTableName( + $tableName, + $resourceName + ); + + /** @var \Magento\Framework\Model\ResourceModel\Db\VersionControl\AbstractDb $resource */ + $resource = $this->fixtureModel->getObjectManager()->get($resourceName); + $connection = $resource->getConnection(); + return (int)$connection->query("SELECT MAX(`{$column}`) FROM `{$tableName}`;")->fetchColumn(0); + } + + /** + * Get a limited amount of product id's from a collection filtered by store and specific product type. + * + * @param \Magento\Store\Api\Data\StoreInterface $store + * @param string $typeId + * @param int $limit + * @return array + */ + private function getProductIds(\Magento\Store\Api\Data\StoreInterface $store, $typeId, $limit = null) + { + /** @var $productCollection \Magento\Catalog\Model\ResourceModel\Product\Collection */ + $productCollection = $this->productCollectionFactory->create(); + + $productCollection->addStoreFilter($store->getId()); + $productCollection->addWebsiteFilter($store->getWebsiteId()); + + // "Big%" should be replaced with a configurable value. + if ($typeId === self::BIG_CONFIGURABLE_TYPE) { + $productCollection->getSelect()->where(" type_id = '" . Configurable::TYPE_CODE . "' "); + $productCollection->getSelect()->where(" sku LIKE 'Big%' "); + } else { + $productCollection->getSelect()->where(" type_id = '$typeId' "); + $productCollection->getSelect()->where(" sku NOT LIKE 'Big%' "); + } + + return $productCollection->getAllIds($limit); + } + + /** + * Prepare data for the simple products to be used as order items. + * + * Based on the Product Id's load data, which is required to replace placeholders in queries. + * + * @param array $productIds + * @return array + */ + private function prepareSimpleProducts(array $productIds = []) + { + $productsResult = []; + foreach ($productIds as $key => $simpleId) { + $simpleProduct = $this->productRepository->getById($simpleId); + $productsResult[$key]['id'] = $simpleId; + $productsResult[$key]['sku'] = $simpleProduct->getSku(); + $productsResult[$key]['name'] = $simpleProduct->getName(); + $productsResult[$key]['buyRequest'] = $this->serializer->serialize([ + "info_buyRequest" => [ + "uenc" => "aHR0cDovL21hZ2VudG8uZGV2L2NvbmZpZ3VyYWJsZS1wcm9kdWN0LTEuaHRtbA,,", + "product" => $simpleId, + "qty" => "1" + ] + ]); + } + return $productsResult; + } + + /** + * Prepare data for the configurable products to be used as order items. + * + * Based on the Product Id's load data, which is required to replace placeholders in queries. + * + * @param array $productIds + * @return array + */ + private function prepareConfigurableProducts(array $productIds = []) + { + $productsResult = []; + foreach ($productIds as $key => $configurableId) { + $configurableProduct = $this->productRepository->getById($configurableId); + $options = $this->optionRepository->getList($configurableProduct->getSku()); + $configurableChild = $this->linkManagement->getChildren($configurableProduct->getSku())[0]; + $simpleSku = $configurableChild->getSku(); + $simpleId = $this->productRepository->get($simpleSku)->getId(); + + $attributesInfo = []; + $superAttribute = []; + foreach ($options as $option) { + $attributesInfo[] = [ + "label" => $option->getLabel(), + "value" => $option['options']['0']['label'], + "option_id" => $option->getAttributeId(), + "option_value" => $option->getValues()[0]->getValueIndex() + ]; + $superAttribute[$option->getAttributeId()] = $option->getValues()[0]->getValueIndex(); + } + + $configurableBuyRequest = [ + "info_buyRequest" => [ + "uenc" => "aHR0cDovL21hZ2UyLmNvbS9jYXRlZ29yeS0xLmh0bWw", + "product" => $configurableId, + "selected_configurable_option" => $simpleId, + "related_product" => "", + "super_attribute" => $superAttribute, + "qty" => 1 + ], + "attributes_info" => $attributesInfo, + "simple_name" => $configurableChild->getName(), + "simple_sku" => $configurableChild->getSku(), + ]; + $simpleBuyRequest = [ + "info_buyRequest" => [ + "uenc" => "aHR0cDovL21hZ2VudG8uZGV2L2NvbmZpZ3VyYWJsZS1wcm9kdWN0LTEuaHRtbA,,", + "product" => $configurableId, + "selected_configurable_option" => $simpleId, + "related_product" => "", + "super_attribute" => $superAttribute, + "qty" => "1" + ] + ]; + + $quoteConfigurableBuyRequest = $configurableBuyRequest['info_buyRequest']; + $quoteSimpleBuyRequest = $simpleBuyRequest['info_buyRequest']; + unset($quoteConfigurableBuyRequest['selected_configurable_option']); + unset($quoteSimpleBuyRequest['selected_configurable_option']); + + $productsResult[$key]['id'] = $configurableId; + $productsResult[$key]['sku'] = $simpleSku; + $productsResult[$key]['name'] = $configurableProduct->getName(); + $productsResult[$key]['childId'] = $simpleId; + $productsResult[$key]['buyRequest'] = [ + 'order' => $this->serializer->serialize($configurableBuyRequest), + 'quote' => $this->serializer->serialize($quoteConfigurableBuyRequest), + 'super_attribute' => $this->serializer->serialize($superAttribute) + ]; + $productsResult[$key]['childBuyRequest'] = [ + 'order' => $this->serializer->serialize($simpleBuyRequest), + 'quote' => $this->serializer->serialize($quoteSimpleBuyRequest), + ]; + + } + return $productsResult; + } + + /** + * Commit all active transactions at the end of the batch. + * + * Many transactions may exist, since generation process creates a transaction per each available DB connection. + * + * @return void + */ + private function commitBatch() + { + foreach ($this->resourceConnections as $connection) { + if ($connection->getTransactionLevel() > 0) { + $connection->commit(); + $connection->beginTransaction(); + } + } } /** @@ -294,12 +693,13 @@ public function getActionTitle() public function introduceParamLabels() { return [ - 'orders' => 'Orders' + 'orders' => 'Orders' ]; } /** - * Get real table name for db table, validated by db adapter + * Get real table name for db table, validated by db adapter. + * In case prefix or other features mutating default table names are used. * * @param string $tableName * @param string $resourceName @@ -307,19 +707,24 @@ public function introduceParamLabels() */ public function getTableName($tableName, $resourceName) { + /** @var \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource */ $resource = $this->fixtureModel->getObjectManager()->get($resourceName); - return $this->getConnection()->getTableName($resource->getTable($tableName)); + return $resource->getConnection()->getTableName($resource->getTable($tableName)); } /** - * Retrieve connection to resource specified by $resourceName + * Get sequence for order items * - * @return \Magento\Framework\DB\Adapter\AdapterInterface|false + * @param int $maxItemId + * @param int $requestedOrders + * @param int $maxItemsPerOrder + * @return \Generator */ - public function getConnection() + private function getItemIdSequence($maxItemId, $requestedOrders, $maxItemsPerOrder) { - return $this->fixtureModel->getObjectManager()->get( - \Magento\Framework\App\ResourceConnection::class - )->getConnection(); + $requestedItems = $requestedOrders * $maxItemsPerOrder; + for ($i = $maxItemId + 1; $i <= $requestedItems; $i++) { + yield $i; + } } } diff --git a/setup/src/Magento/Setup/Fixtures/PriceProvider.php b/setup/src/Magento/Setup/Fixtures/PriceProvider.php new file mode 100644 index 0000000000000..de091361884a1 --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/PriceProvider.php @@ -0,0 +1,34 @@ +productCollectionFactory = $collectionFactory; + } + + /** + * Get amount of products filtered by specified pattern + * + * @param int $requestedProducts + * @param string $productSkuPattern + * @return int + */ + public function getAmount($requestedProducts, $productSkuPattern) + { + $productSkuPattern = str_replace('%s', '[0-9]+', $productSkuPattern); + $productCollection = $this->productCollectionFactory->create(); + $productCollection + ->getSelect() + ->where('sku ?', new \Zend_Db_Expr('REGEXP \'^' . $productSkuPattern . '$\'')); + + return max(0, $requestedProducts - $productCollection->getSize()); + } +} diff --git a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php index 6de4eaf91529f..b712238cfc47e 100644 --- a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php +++ b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php @@ -1,158 +1,391 @@ {products amount} + * Products will be distributed per Default and pre-defined + * attribute sets (@see setup/performance-toolkit/config/attributeSets.xml) + * + * If extra attribute set is specified in profile as: {sets amount} + * then products also will be distributed per additional attribute sets + * + * Products will be uniformly distributed per categories and websites + * If node "assign_entities_to_all_websites" from profile is set to "1" then products will be assigned to all websites + * + * @see setup/performance-toolkit/profiles/ce/small.xml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SimpleProductsFixture extends Fixture { + /** + * Simple product sku pattern + */ + const SKU_PATTERN = 'product_dynamic_%s'; + + /** + * @var int + */ + protected $priority = 31; + + /** + * @var array + */ + private $descriptionConfig; + + /** + * @var array + */ + private $shortDescriptionConfig; + + /** + * @var ProductFactory + */ + private $productFactory; + + /** + * @var ProductGenerator + */ + private $productGenerator; + /** * @var int */ - protected $priority = 30; + private $defaultAttributeSetId; + + /** + * @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection + */ + private $attributeCollectionFactory; + + /** + * @var AttributeSetCollectionFactory + */ + private $attributeSetCollectionFactory; + + /** + * @var SearchTermDescriptionGeneratorFactory + */ + private $descriptionGeneratorFactory; + + /** + * @var ProductsAmountProvider + */ + private $productsAmountProvider; + + /** + * @var WebsiteCategoryProvider + */ + private $websiteCategoryProvider; + + /** + * @var PriceProvider + */ + private $priceProvider; + + /** + * @param FixtureModel $fixtureModel + * @param ProductFactory $productFactory + * @param ProductGenerator $productGenerator + * @param CollectionFactory $attributeCollectionFactory + * @param AttributeSetCollectionFactory $attributeSetCollectionFactory + * @param SearchTermDescriptionGeneratorFactory $descriptionGeneratorFactory + * @param WebsiteCategoryProvider $websiteCategoryProvider + * @param ProductsAmountProvider $productsAmountProvider + * @param PriceProvider $priceProvider + * @internal param FixtureConfig $fixtureConfig + */ + public function __construct( + FixtureModel $fixtureModel, + ProductFactory $productFactory, + ProductGenerator $productGenerator, + CollectionFactory $attributeCollectionFactory, + AttributeSetCollectionFactory $attributeSetCollectionFactory, + SearchTermDescriptionGeneratorFactory $descriptionGeneratorFactory, + WebsiteCategoryProvider $websiteCategoryProvider, + ProductsAmountProvider $productsAmountProvider, + PriceProvider $priceProvider + ) { + parent::__construct($fixtureModel); + $this->productFactory = $productFactory; + $this->productGenerator = $productGenerator; + $this->attributeCollectionFactory = $attributeCollectionFactory; + $this->attributeSetCollectionFactory = $attributeSetCollectionFactory; + $this->descriptionGeneratorFactory = $descriptionGeneratorFactory; + $this->productsAmountProvider = $productsAmountProvider; + $this->websiteCategoryProvider = $websiteCategoryProvider; + $this->priceProvider = $priceProvider; + } + + /** + * {@inheritdoc} + */ + public function getActionTitle() + { + return 'Generating simple products'; + } /** * {@inheritdoc} */ + public function introduceParamLabels() + { + return [ + 'simple_products' => 'Simple products' + ]; + } + + /** + * {@inheritdoc} + * @SuppressWarnings(PHPMD) + */ public function execute() { - $simpleProductsCount = $this->fixtureModel->getValue('simple_products', 0); + $simpleProductsCount = $this->productsAmountProvider->getAmount( + $this->fixtureModel->getValue('simple_products', 0), + $this->getSkuPattern() + ); + if (!$simpleProductsCount) { return; } - $this->fixtureModel->resetObjectManager(); - - /** @var \Magento\Store\Model\StoreManager $storeManager */ - $storeManager = $this->fixtureModel->getObjectManager()->create(\Magento\Store\Model\StoreManager::class); - /** @var $category \Magento\Catalog\Model\Category */ - $category = $this->fixtureModel->getObjectManager()->get(\Magento\Catalog\Model\Category::class); - - $result = []; - //Get all websites - $websites = $storeManager->getWebsites(); - foreach ($websites as $website) { - $websiteCode = $website->getCode(); - //Get all groups - $websiteGroups = $website->getGroups(); - foreach ($websiteGroups as $websiteGroup) { - $websiteGroupRootCategory = $websiteGroup->getRootCategoryId(); - $category->load($websiteGroupRootCategory); - $categoryResource = $category->getResource(); - //Get all categories - $resultsCategories = $categoryResource->getAllChildren($category); - foreach ($resultsCategories as $resultsCategory) { - $category->load($resultsCategory); - $structure = explode('/', $category->getPath()); - $pathSize = count($structure); - if ($pathSize > 1) { - $path = []; - for ($i = 0; $i < $pathSize; $i++) { - $path[] = $category->load($structure[$i])->getName(); + + $defaultAttributeSets = $this->getDefaultAttributeSets(); + $searchTermsConfig = $this->getSearchTerms(); + + /** @var \Magento\Setup\Model\SearchTermDescriptionGenerator $descriptionGenerator */ + $descriptionGenerator = $this->descriptionGeneratorFactory->create( + $this->getDescriptionConfig(), + $searchTermsConfig, + $simpleProductsCount, + 'Full simple product Description %s' + ); + + /** @var \Magento\Setup\Model\SearchTermDescriptionGenerator $shortDescriptionGenerator */ + $shortDescriptionGenerator = $this->descriptionGeneratorFactory->create( + $this->getShortDescriptionConfig(), + $searchTermsConfig, + $simpleProductsCount, + 'Short simple product Description %s' + ); + + $additionalAttributeSets = $this->getAdditionalAttributeSets(); + $attributeSet = function ($index) use ($defaultAttributeSets, $additionalAttributeSets) { + mt_srand($index); + $attributeSetCount = count(array_keys($defaultAttributeSets)); + if ($attributeSetCount > (($index - 1) % (int)$this->fixtureModel->getValue('categories', 30))) { + return array_keys($defaultAttributeSets)[mt_rand(0, count(array_keys($defaultAttributeSets)) - 1)]; + } else { + $customSetsAmount = count($additionalAttributeSets); + return $customSetsAmount + ? $additionalAttributeSets[$index % count($additionalAttributeSets)]['attribute_set_id'] + : $this->getDefaultAttributeSetId(); + } + }; + + $additionalAttributes = function ( + $attributeSetId, + $index + ) use ( + $defaultAttributeSets, + $additionalAttributeSets + ) { + $attributeValues = []; + mt_srand($index); + if (isset($defaultAttributeSets[$attributeSetId])) { + foreach ($defaultAttributeSets[$attributeSetId] as $attributeCode => $values) { + $attributeValues[$attributeCode] = $values[mt_rand(0, count($values) - 1)]; + } + } + + return $attributeValues; + }; + + $fixtureMap = [ + 'name' => function ($productId) { + return sprintf('Simple Product %s', $productId); + }, + 'sku' => function ($productId) { + return sprintf($this->getSkuPattern(), $productId); + }, + 'price' => function ($index, $entityNumber) { + return $this->priceProvider->getPrice($entityNumber); + }, + 'url_key' => function ($productId) { + return sprintf('simple-product-%s', $productId); + }, + 'description' => function ($index) use ($descriptionGenerator) { + return $descriptionGenerator->generate($index); + }, + 'short_description' => function ($index) use ($shortDescriptionGenerator) { + return $shortDescriptionGenerator->generate($index); + }, + 'website_ids' => function ($index, $entityNumber) { + return $this->websiteCategoryProvider->getWebsiteIds($index); + }, + 'category_ids' => function ($index, $entityNumber) { + return $this->websiteCategoryProvider->getCategoryId($index); + }, + 'attribute_set_id' => $attributeSet, + 'additional_attributes' => $additionalAttributes, + 'status' => function () { + return Status::STATUS_ENABLED; + } + ]; + $this->productGenerator->generate($simpleProductsCount, $fixtureMap); + } + + /** + * Get simple product sku pattern + * + * @return string + */ + private function getSkuPattern() + { + return self::SKU_PATTERN; + } + + /** + * Get default attribute set id + * + * @return int + */ + private function getDefaultAttributeSetId() + { + if (null === $this->defaultAttributeSetId) { + $this->defaultAttributeSetId = (int)$this->productFactory->create()->getDefaultAttributeSetId(); + } + + return $this->defaultAttributeSetId; + } + + /** + * Get default attribute sets with attributes + * + * @see config/attributeSets.xml + * @return array + */ + private function getDefaultAttributeSets() + { + $attributeSets = $this->fixtureModel->getValue('attribute_sets', null); + $attributes = []; + + if ($attributeSets !== null && array_key_exists('attribute_set', $attributeSets)) { + foreach ($attributeSets['attribute_set'] as $attributeSet) { + $attributesData = array_key_exists(0, $attributeSet['attributes']['attribute']) + ? $attributeSet['attributes']['attribute'] : [$attributeSet['attributes']['attribute']]; + + $attributeCollection = $this->attributeCollectionFactory->create(); + + $attributeCollection->setAttributeSetFilterBySetName($attributeSet['name'], Product::ENTITY); + $attributeCollection->addFieldToFilter( + 'attribute_code', + array_column($attributesData, 'attribute_code') + ); + /** @var \Magento\Eav\Model\Entity\Attribute $attribute */ + foreach ($attributeCollection as $attribute) { + $values = []; + $options = $attribute->getOptions(); + foreach (($options ?: []) as $option) { + if ($option->getValue()) { + $values[] = $option->getValue(); } - array_shift($path); - $resultsCategoryName = implode('/', $path); - } else { - $resultsCategoryName = $category->getName(); - } - //Deleted root categories - if (trim($resultsCategoryName) != '') { - $result[$resultsCategory] = [$websiteCode, $resultsCategoryName]; } + $attributes[$attribute->getAttributeSetId()][$attribute->getAttributeCode()] = $values; } } } - $result = array_values($result); + $attributes[$this->getDefaultAttributeSetId()] = []; - $productWebsite = function ($index) use ($result) { - return $result[$index % count($result)][0]; - }; - $productCategory = function ($index) use ($result) { - return $result[$index % count($result)][1]; - }; + return $attributes; + } - $generator = new Generator( - $this->getPattern($productWebsite, $productCategory), - $simpleProductsCount - ); - /** @var \Magento\ImportExport\Model\Import $import */ - $import = $this->fixtureModel->getObjectManager()->create( - \Magento\ImportExport\Model\Import::class, - [ - 'data' => [ - 'entity' => 'catalog_product', - 'behavior' => 'append', - 'validation_strategy' => 'validation-stop-on-errors' - ] - ] - ); - // it is not obvious, but the validateSource() will actually save import queue data to DB - $import->validateSource($generator); - // this converts import queue into actual entities - $import->importSource(); + /** + * Get search terms config which used for product description generation + * + * @return array + */ + private function getSearchTerms() + { + $searchTerms = $this->fixtureModel->getValue('search_terms', []); + if (!empty($searchTerms)) { + $searchTerms = array_key_exists(0, $searchTerms['search_term']) + ? $searchTerms['search_term'] : [$searchTerms['search_term']]; + } + + return $searchTerms; } /** - * Get pattern for product import + * Get description config * - * @param Closure|int|string $productWebsiteClosure - * @param Closure|int|string $productCategoryClosure * @return array */ - protected function getPattern($productWebsiteClosure, $productCategoryClosure) + private function getDescriptionConfig() { - return [ - 'attribute_set_code' => 'Default', - 'product_type' => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE, - 'product_websites' => $productWebsiteClosure, - 'categories' => $productCategoryClosure, - 'name' => 'Simple Product %s', - 'short_description' => 'Short simple product description %s', - 'weight' => 1, - 'description' => 'Full simple product Description %s', - 'sku' => 'product_dynamic_%s', - 'price' => 10, - 'visibility' => 'Catalog, Search', - 'product_online' => \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED, - 'tax_class_name' => 'Taxable Goods', - /** - * actually it saves without stock data, but by default system won't show on the - * frontend products out of stock - */ - 'is_in_stock' => 1, - 'qty' => 100500, - 'out_of_stock_qty' => 'Use Config', - 'allow_backorders' => 'Use Config', - 'min_cart_qty' => 'Use Config', - 'max_cart_qty' => 'Use Config', - 'notify_on_stock_below' => 'Use Config', - 'manage_stock' => 'Use Config', - 'qty_increments' => 'Use Config', - 'enable_qty_incremements' => 'Use Config', - ]; + if (null === $this->descriptionConfig) { + $this->descriptionConfig = $this->readDescriptionConfig('description'); + } + + return $this->descriptionConfig; } /** - * {@inheritdoc} + * Get short description config + * + * @return array */ - public function getActionTitle() + private function getShortDescriptionConfig() { - return 'Generating simple products'; + if (null === $this->shortDescriptionConfig) { + $this->shortDescriptionConfig = $this->readDescriptionConfig('short-description'); + } + + return $this->shortDescriptionConfig; } /** - * {@inheritdoc} + * Get description config from fixture + * + * @param string $configSrc + * @return array */ - public function introduceParamLabels() + private function readDescriptionConfig($configSrc) { - return [ - 'simple_products' => 'Simple products' - ]; + $configData = $this->fixtureModel->getValue($configSrc, []); + + if (isset($configData['mixin']['tags'])) { + $configData['mixin']['tags'] = explode('|', $configData['mixin']['tags']); + } + + return $configData; + } + + /** + * Get additional attribute sets + * + * @return \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection[] + */ + private function getAdditionalAttributeSets() + { + /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection $sets */ + $sets = $this->attributeSetCollectionFactory->create(); + $sets->addFieldToFilter('attribute_set_name', ['like' => AttributeSetsFixture::PRODUCT_SET_NAME .'%']); + + return $sets->getData(); } } diff --git a/setup/src/Magento/Setup/Fixtures/StoresFixture.php b/setup/src/Magento/Setup/Fixtures/StoresFixture.php index dcd7dd5b6f50c..4103fdcafd534 100644 --- a/setup/src/Magento/Setup/Fixtures/StoresFixture.php +++ b/setup/src/Magento/Setup/Fixtures/StoresFixture.php @@ -1,161 +1,354 @@ {amount of websites} + * {amount of store groups} + * {amount of store views} + * {1|0} + * + * Each node of configuration except + * means how many entities need to be generated + * + * Store groups will have normal distribution among websites + * Store views will have normal distribution among store groups + * + * 1 + * means that all stores will have the same root category + * + * 1 + * means that all stores will have unique root category + * + * @see setup/performance-toolkit/profiles/ce/small.xml + * @SuppressWarnings(PHPMD) */ class StoresFixture extends Fixture { + + const DEFAULT_WEBSITE_COUNT = 1; + + const DEFAULT_STORE_COUNT = 1; + + const DEFAULT_STORE_VIEW_COUNT = 1; + /** * @var int */ protected $priority = 10; + /** + * @var array + */ + private $websiteIds = []; + + /** + * @var array + */ + private $storeGroupsIds = []; + + /** + * @var array + */ + private $storeGroupsToWebsites = []; + + /** + * @var StoreManager + */ + private $storeManager; + + /** + * @var Writer + */ + private $scopeConfig; + + /** + * @var Group + */ + private $defaultStoreGroup; + + /** + * @var Website + */ + private $defaultWebsite; + + /** + * @var int + */ + private $defaultParentCategoryId; + + /** + * @var int + */ + private $defaultStoreGroupId; + + /** + * @var int + */ + private $defaultWebsiteId; + + /** + * @var int + */ + private $storeGroupsCount; + + /** + * @var int + */ + private $storesCount; + + /** + * @var int + */ + private $websitesCount; + + /** + * @var bool + */ + private $singleRootCategory; + + /** + * @var StoreInterface + */ + private $defaultStoreView; + + /** + * @var int + */ + private $storeViewIds; + + /** + * @var ManagerInterface + */ + private $eventManager; + + /** + * @var CategoryFactory + */ + private $categoryFactory; + + /** + * @var Config + */ + private $localeConfig; + + /** + * StoresFixture constructor + * @param FixtureModel $fixtureModel + * @param StoreManager $storeManager + * @param ManagerInterface $eventManager + * @param CategoryFactory $categoryFactory + * @param Config $localeConfig + * @param Writer $scopeConfig + */ + public function __construct( + FixtureModel $fixtureModel, + StoreManager $storeManager, + ManagerInterface $eventManager, + CategoryFactory $categoryFactory, + Config $localeConfig, + Writer $scopeConfig + ) { + parent::__construct($fixtureModel); + $this->storeManager = $storeManager; + $this->eventManager = $eventManager; + $this->categoryFactory = $categoryFactory; + $this->localeConfig = $localeConfig; + $this->scopeConfig = $scopeConfig; + } + /** * {@inheritdoc} * @SuppressWarnings(PHPMD) */ public function execute() { - $websitesCount = $this->fixtureModel->getValue('websites', 0); - $storeGroupsCount = $this->fixtureModel->getValue('store_groups', 0); - $storesCount = $this->fixtureModel->getValue('store_views', 0); - if (!$websitesCount || !$storeGroupsCount || !$storesCount) { + //get settings counts + $this->websitesCount = $this->fixtureModel->getValue('websites', self::DEFAULT_WEBSITE_COUNT); + $this->storeGroupsCount = $this->fixtureModel->getValue('store_groups', self::DEFAULT_STORE_COUNT); + $this->storesCount = $this->fixtureModel->getValue('store_views', self::DEFAULT_STORE_VIEW_COUNT); + $this->singleRootCategory = (bool)$this->fixtureModel->getValue('assign_entities_to_all_websites', false); + + if ( + $this->websitesCount <= self::DEFAULT_WEBSITE_COUNT + && $this->storeGroupsCount <= self::DEFAULT_STORE_COUNT + && $this->storesCount <= self::DEFAULT_STORE_VIEW_COUNT + ) { return; } - $this->fixtureModel->resetObjectManager(); - - /** @var \Magento\Store\Model\StoreManager $storeManager */ - $storeManager = $this->fixtureModel->getObjectManager()->create(\Magento\Store\Model\StoreManager::class); - /** @var $category \Magento\Catalog\Model\Category */ - - /** @var $defaultWebsite \Magento\Store\Model\Website */ - $defaultWebsite = $storeManager->getWebsite(); - /** @var $defaultStoreGroup \Magento\Store\Model\Group */ - $defaultStoreGroup = $storeManager->getGroup(); - /** @var $defaultStoreView \Magento\Store\Model\Store */ - $defaultStoreView = $storeManager->getDefaultStoreView(); - - $defaultParentCategoryId = $storeManager->getStore()->getRootCategoryId(); - - $defaultWebsiteId = $defaultWebsite->getId(); - $defaultStoreGroupId = $defaultStoreGroup->getId(); - $defaultStoreViewId = $defaultStoreView->getId(); - - $websitesId = []; - $groupsId = []; - - //Create $websitesCount websites - for ($i = 0; $i < $websitesCount; $i++) { - $websiteId = null; - if ($i == 0) { - $websiteId = $defaultWebsiteId; - } - $website = clone $defaultWebsite; - $websiteCode = sprintf('website_%d', $i + 1); - $websiteName = sprintf('Website %d', $i + 1); + + //Get existing entities counts + $storeGroups = $this->storeManager->getGroups(); + $this->storeGroupsIds= array_keys($storeGroups); + + foreach ($storeGroups as $storeGroupId => $storeGroup) { + $this->storeGroupsToWebsites[$storeGroupId] = $storeGroup->getWebsiteId(); + } + + $this->websiteIds = array_values(array_unique($this->storeGroupsToWebsites)); + + $this->defaultWebsite = $this->storeManager->getWebsite(); + $this->defaultStoreGroup = $this->storeManager->getGroup(); + $this->defaultWebsiteId = $this->defaultWebsite->getId(); + $this->defaultStoreGroupId = $this->defaultStoreGroup->getId(); + $this->defaultStoreView = $this->storeManager->getDefaultStoreView(); + $this->storeViewIds = array_keys($this->storeManager->getStores()); + + $this->generateWebsites(); + $this->generateStoreGroups(); + $this->generateStoreViews(); + + //clean cache + $this->storeManager->reinitStores(); + } + + /** + * Generating web sites + * @return void + */ + private function generateWebsites() + { + $existedWebsitesCount = count($this->websiteIds) + self::DEFAULT_WEBSITE_COUNT; + + while ($existedWebsitesCount <= $this->websitesCount) { + $website = clone $this->defaultWebsite; + $websiteCode = sprintf('website_%d', $existedWebsitesCount); + $websiteName = sprintf('Website %d', $existedWebsitesCount); $website->addData( [ - 'website_id' => $websiteId, - 'code' => $websiteCode, - 'name' => $websiteName, - 'is_default' => (int)$i == 0, + 'website_id' => null, + 'code' => $websiteCode, + 'name' => $websiteName, + 'is_default' => false, ] ); $website->save(); - $websitesId[$i] = $website->getId(); - usleep(20); + $this->websiteIds[] = $website->getId(); + $existedWebsitesCount++; } + } - //Create $storeGroupsCount websites - $websiteNumber = 0; - for ($i = 0; $i < $storeGroupsCount; $i++) { - $category = $this->fixtureModel->getObjectManager()->create(\Magento\Catalog\Model\Category::class); - $websiteId = $websitesId[$websiteNumber]; - $groupId = null; - $categoryPath = '1'; - - $storeGroupName = sprintf('Store Group %d - website_id_%d', $i + 1, $websiteId); - - if ($i == 0 && $websiteId == $defaultWebsiteId) { - $groupId = $defaultStoreGroupId; - $categoryPath = '1/' . $defaultParentCategoryId; - $category->load($defaultParentCategoryId); - } + /** + * Generating store groups ('stores' on frontend) + * @return void + */ + private function generateStoreGroups() + { + $existedStoreGroupCount = count($this->storeGroupsIds); + $existedWebsitesCount = count($this->websiteIds); - $category->setName("Category $storeGroupName") - ->setPath($categoryPath) - ->setLevel(1) - ->setAvailableSortBy('name') - ->setDefaultSortBy('name') - ->setIsActive(true) - ->save(); + while ($existedStoreGroupCount < $this->storeGroupsCount) { + $websiteId = $this->websiteIds[$existedStoreGroupCount % $existedWebsitesCount]; + $storeGroupName = sprintf('Store Group %d - website_id_%d', ++$existedStoreGroupCount, $websiteId); - $storeGroup = clone $defaultStoreGroup; + $storeGroup = clone $this->defaultStoreGroup; $storeGroup->addData( [ - 'group_id' => $groupId, - 'website_id' => $websiteId, - 'name' => $storeGroupName, - 'root_category_id' => $category->getId(), + 'group_id' => null, + 'website_id' => $websiteId, + 'name' => $storeGroupName, + 'root_category_id' => $this->getStoreCategoryId($storeGroupName), ] ); $storeGroup->save(); - $groupsId[$websiteId][] = $storeGroup->getId(); - - $websiteNumber++; - if ($websiteNumber == count($websitesId)) { - $websiteNumber = 0; - } - usleep(20); + $this->storeGroupsIds[] = $storeGroup->getId(); + $this->storeGroupsToWebsites[$storeGroup->getId()] = $websiteId; } + } + + /** + * Generating store views + * @return void + */ + private function generateStoreViews() + { + $localesList = $this->localeConfig->getAllowedLocales(); + $localesListCount = count($localesList); + + $existedStoreViewsCount = count($this->storeViewIds); + $existedStoreGroupCount = count($this->storeGroupsIds); + + while ($existedStoreViewsCount < $this->storesCount) { + $groupId = $this->storeGroupsIds[$existedStoreViewsCount % $existedStoreGroupCount]; + $websiteId = $this->storeGroupsToWebsites[$groupId]; + $store = clone $this->defaultStoreView; + $storeCode = sprintf('store_view_%d', ++$existedStoreViewsCount); + $storeName = sprintf( + 'Store view %d - website_id_%d - group_id_%d', + $existedStoreViewsCount, + $websiteId, + $groupId + ); - //Create $storesCount stores - $websiteNumber = 0; - $groupNumber = 0; - for ($i = 0; $i < $storesCount; $i++) { - $websiteId = $websitesId[$websiteNumber]; - $groupId = $groupsId[$websiteId][$groupNumber]; - $storeId = null; - if ($i == 0 && $groupId == $defaultStoreGroupId) { - $storeId = $defaultStoreViewId; - } - $store = clone $defaultStoreView; - $storeCode = sprintf('store_view_%d_w_%d_g_%d', $i + 1, $websiteId, $groupId); - $storeName = sprintf('Store view %d - website_id_%d - group_id_%d', $i + 1, $websiteId, $groupId); $store->addData( [ - 'store_id' => $storeId, - 'name' => $storeName, - 'website_id' => $websiteId, - 'group_id' => $groupId, + 'store_id' => null, + 'name' => $storeName, + 'website_id' => $websiteId, + 'group_id' => $groupId, + 'code' => $storeCode ] - ); + )->save(); + $this->eventManager->dispatch('store_add', ['store' => $store]); - if ($storeId == null) { - $store->addData( - [ - 'code' => $storeCode, - ] - ); - } - - $store->save(); - - $groupNumber++; - if ($groupNumber == count($groupsId[$websiteId])) { - $groupNumber = 0; - $websiteNumber++; - if ($websiteNumber == count($websitesId)) { - $websiteNumber = 0; - } - } - usleep(20); + $this->saveStoreLocale($store->getId(), $localesList[$existedStoreViewsCount % $localesListCount]); + } + } + + /** + * @param int $storeId + * @param string $localeCode + * @return void + */ + private function saveStoreLocale($storeId, $localeCode) + { + $this->scopeConfig->save( + \Magento\Directory\Helper\Data::XML_PATH_DEFAULT_LOCALE, + $localeCode, + \Magento\Store\Model\ScopeInterface::SCOPE_STORES, + $storeId + ); + } + + /** + * Getting category id for store + * + * @param string $storeGroupName + * @return int + */ + private function getStoreCategoryId($storeGroupName) + { + if ($this->singleRootCategory) { + return $this->getDefaultCategoryId(); + } else { + //Generating category for store + $category = $this->categoryFactory->create(); + $categoryPath = Category::TREE_ROOT_ID; + $category->setName("Category " . $storeGroupName) + ->setPath($categoryPath) + ->setLevel(1) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->save(); + + return $category->getId(); } } @@ -173,9 +366,22 @@ public function getActionTitle() public function introduceParamLabels() { return [ - 'websites' => 'Websites', - 'store_groups' => 'Store Groups', - 'store_views' => 'Store Views' + 'websites' => 'Websites', + 'store_groups' => 'Store Groups Count', + 'store_views' => 'Store Views Count' ]; } + + /** + * Get default category id + * + * @return int + */ + private function getDefaultCategoryId() + { + if (null === $this->defaultParentCategoryId) { + $this->defaultParentCategoryId = $this->storeManager->getStore()->getRootCategoryId(); + } + return $this->defaultParentCategoryId; + } } diff --git a/setup/src/Magento/Setup/Fixtures/TaxRatesFixture.php b/setup/src/Magento/Setup/Fixtures/TaxRatesFixture.php index e024b47bc568a..baa5209ab952c 100644 --- a/setup/src/Magento/Setup/Fixtures/TaxRatesFixture.php +++ b/setup/src/Magento/Setup/Fixtures/TaxRatesFixture.php @@ -1,6 +1,6 @@ fixtureModel->resetObjectManager(); /** Clean predefined tax rates to maintain consistency */ - /** @var $collection Magento\Tax\Model\ResourceModel\Calculation\Rate\Collection */ + /** @var $collection \Magento\Tax\Model\ResourceModel\Calculation\Rate\Collection */ $collection = $this->fixtureModel->getObjectManager() ->get(\Magento\Tax\Model\ResourceModel\Calculation\Rate\Collection::class); - /** @var $model Magento\Tax\Model\Calculation\Rate */ + /** @var $model \Magento\Tax\Model\Calculation\Rate */ $model = $this->fixtureModel->getObjectManager() ->get(\Magento\Tax\Model\Calculation\Rate::class); @@ -42,7 +42,7 @@ public function execute() /** * Import tax rates with import handler */ - $filename = realpath(__DIR__ . '/' . $taxRatesFile); + $filename = realpath(__DIR__ . DIRECTORY_SEPARATOR . "_files" . DIRECTORY_SEPARATOR . $taxRatesFile); $file = [ 'name' => $filename, 'type' => 'fixtureModel/vnd.ms-excel', diff --git a/setup/src/Magento/Setup/Fixtures/TaxRulesFixture.php b/setup/src/Magento/Setup/Fixtures/TaxRulesFixture.php new file mode 100644 index 0000000000000..a60145940953b --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/TaxRulesFixture.php @@ -0,0 +1,226 @@ + [ + Config::CONFIG_XML_PATH_SHIPPING_INCLUDES_TAX => 1, + Config::CONFIG_XML_PATH_PRICE_INCLUDES_TAX => 1, + Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 1, + Config::XML_PATH_DISPLAY_SALES_PRICE => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::XML_PATH_DISPLAY_SALES_SUBTOTAL => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::XML_PATH_DISPLAY_SALES_SHIPPING => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::XML_PATH_DISPLAY_SALES_DISCOUNT => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::XML_PATH_DISPLAY_SALES_GRANDTOTAL => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::XML_PATH_DISPLAY_SALES_FULL_SUMMARY => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::CONFIG_XML_PATH_DISPLAY_SHIPPING => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::CONFIG_XML_PATH_PRICE_DISPLAY_TYPE => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::XML_PATH_DISPLAY_CART_PRICE => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::XML_PATH_DISPLAY_CART_SUBTOTAL => Config::DISPLAY_TYPE_INCLUDING_TAX, + Config::XML_PATH_DISPLAY_CART_SHIPPING => Config::DISPLAY_TYPE_INCLUDING_TAX, + Custom::XML_PATH_TAX_WEEE_ENABLE => 1, + ] + ]; + + /** + * @var int + */ + protected $priority = 101; + + /** + * @var TaxRuleRepositoryInterface + */ + private $taxRuleRepository; + + /** + * @var TaxRuleInterfaceFactory + */ + private $taxRuleFactory; + + /** + * @var TaxRateInterfaceFactory + */ + private $taxRateFactory; + + /** + * @var CollectionFactory + */ + private $taxRateCollectionFactory; + + /** + * @var TaxRateRepositoryInterface + */ + private $taxRateRepository; + + /** + * @var ConfigWriter + */ + private $configWriter; + + /** + * @param FixtureModel $fixtureModel + * @param TaxRuleRepositoryInterface $taxRuleRepository + * @param TaxRuleInterfaceFactory $taxRuleFactory + * @param CollectionFactory $taxRateCollectionFactory + * @param TaxRateInterfaceFactory $taxRateFactory + * @param TaxRateRepositoryInterface $taxRateRepository + * @param ConfigWriter $configWriter + */ + public function __construct( + FixtureModel $fixtureModel, + TaxRuleRepositoryInterface $taxRuleRepository, + TaxRuleInterfaceFactory $taxRuleFactory, + CollectionFactory $taxRateCollectionFactory, + TaxRateInterfaceFactory $taxRateFactory, + TaxRateRepositoryInterface $taxRateRepository, + ConfigWriter $configWriter + ) { + parent::__construct($fixtureModel); + + $this->taxRuleRepository = $taxRuleRepository; + $this->taxRuleFactory = $taxRuleFactory; + $this->taxRateCollectionFactory = $taxRateCollectionFactory; + $this->taxRateFactory = $taxRateFactory; + $this->taxRateRepository = $taxRateRepository; + $this->configWriter = $configWriter; + } + + /** + * {@inheritdoc} + */ + public function execute() + { + //Getting config values + $taxMode = $this->fixtureModel->getValue('tax_mode', null); + $taxRules = $this->fixtureModel->getValue('tax_rules', 0); + + if ($taxMode && in_array($taxMode, array_keys($this->configs))) { + $this->setTaxMode($taxMode); + } + + $taxRateIds = $this->taxRateCollectionFactory->create()->getAllIds(); + $taxRatesCount = count($taxRateIds); + + while ($taxRules) { + /** @var $taxRuleDataObject TaxRuleInterface */ + $taxRuleDataObject = $this->taxRuleFactory->create(); + $taxRuleDataObject->setCode('Tax_Rule_' . $taxRules) + ->setTaxRateIds([$taxRateIds[$taxRules % $taxRatesCount]]) + ->setCustomerTaxClassIds([self::DEFAULT_CUSTOMER_TAX_CLASS_ID]) + ->setProductTaxClassIds([self::DEFAULT_PRODUCT_TAX_CLASS_ID]) + ->setPriority(0) + ->setPosition(0); + + $this->taxRuleRepository->save($taxRuleDataObject); + + $taxRules--; + } + } + + /** + * Adding appropriate Tax Rate, Tax Rule and Config Settings for selected Tax Mode (for example EU/VAT) + * + * @param string $taxMode + * @return void + */ + private function setTaxMode($taxMode) + { + //Add Tax Rate for selected Tax Mode + /** @var $taxRate TaxRateInterface */ + $taxRate = $this->taxRateFactory->create(); + $taxRate->setCode($taxMode) + ->setRate(self::DEFAULT_TAX_RATE) + ->setTaxCountryId(self::DEFAULT_TAX_COUNTRY) + ->setTaxPostcode('*'); + + $taxRateData = $this->taxRateRepository->save($taxRate); + + //Add Tax Rule for Tax Mode + /** @var $taxRuleDataObject TaxRuleInterface */ + $taxRuleDataObject = $this->taxRuleFactory->create(); + $taxRuleDataObject->setCode($taxMode) + ->setTaxRateIds([$taxRateData->getId()]) + ->setCustomerTaxClassIds([self::DEFAULT_CUSTOMER_TAX_CLASS_ID]) + ->setProductTaxClassIds([self::DEFAULT_PRODUCT_TAX_CLASS_ID]) + ->setPriority(0) + ->setPosition(0); + + $this->taxRuleRepository->save($taxRuleDataObject); + + //Set Tax Mode configs + $this->setConfigByTaxMode($taxMode); + } + + /** + * Set appropriate Tax Config Settings for selected Tax Mode + * + * @param string $mode + * @return void + */ + private function setConfigByTaxMode($mode = self::DEFAULT_TAX_MODE) + { + if (isset($this->configs[$mode]) && is_array($this->configs[$mode])) { + foreach ($this->configs[$mode] as $configPath => $value) { + $this->configWriter->save( + $configPath, + $value + ); + } + } + } + + /** + * {@inheritdoc} + */ + public function getActionTitle() + { + return 'Generating tax rules'; + } + + /** + * {@inheritdoc} + */ + public function introduceParamLabels() + { + return [ + 'tax_rules' => 'Tax Rules Count', + 'tax_mode' => 'Tax Mode', + ]; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/WebsiteCategoryProvider.php b/setup/src/Magento/Setup/Fixtures/WebsiteCategoryProvider.php new file mode 100644 index 0000000000000..e96ec9ce57c98 --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/WebsiteCategoryProvider.php @@ -0,0 +1,155 @@ +fixtureConfig = $fixtureConfig; + $this->resourceConnection = $resourceConnection; + } + + /** + * Get websites for $productIndex product + * + * @param int $productIndex Index of generated product + * @return array + */ + public function getWebsiteIds($productIndex) + { + if ($this->isAssignToAllWebsites()) { + return $this->getAllWebsites(); + } else { + $categoriesPerWebsite = $this->getCategoriesAndWebsites(); + return [$categoriesPerWebsite[$productIndex % count($categoriesPerWebsite)]['website']]; + } + } + + /** + * Get product if for $productIndex product + * + * @param int $productIndex + * @return int + */ + public function getCategoryId($productIndex) + { + if ($this->isAssignToAllWebsites()) { + $categories = $this->getAllCategories(); + return $categories[$productIndex % count($categories)]; + } else { + $categoriesPerWebsite = $this->getCategoriesAndWebsites(); + return $categoriesPerWebsite[$productIndex % count($categoriesPerWebsite)]['category']; + } + } + + /** + * @return array + */ + private function getCategoriesAndWebsites() + { + if (null === $this->categoriesPerWebsite) { + $select = $this->getConnection()->select() + ->from( + ['c' => $this->resourceConnection->getTableName('catalog_category_entity')], + ['category' => 'entity_id'] + )->join( + ['sg' => $this->resourceConnection->getTableName('store_group')], + "c.path like concat('1/', sg.root_category_id, '/%')", + ['website' => 'website_id'] + ); + $this->categoriesPerWebsite = $this->getConnection()->fetchAll($select); + } + + return $this->categoriesPerWebsite; + } + + /** + * @return bool + */ + private function isAssignToAllWebsites() + { + return (bool)$this->fixtureConfig->getValue('assign_entities_to_all_websites', false); + } + + /** + * @return array + */ + private function getAllWebsites() + { + if (null === $this->websites) { + $this->websites = array_unique(array_column($this->getCategoriesAndWebsites(), 'website')); + } + + return $this->websites; + } + + /** + * @return array + */ + private function getAllCategories() + { + if (null === $this->categories) { + $this->categories = array_values(array_unique(array_column($this->getCategoriesAndWebsites(), 'category'))); + } + + return $this->categories; + } + + /** + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ + private function getConnection() + { + if (null === $this->connection) { + $this->connection = $this->resourceConnection->getConnection(); + } + + return $this->connection; + } +} diff --git a/setup/src/Magento/Setup/Fixtures/_files/dictionary.csv b/setup/src/Magento/Setup/Fixtures/_files/dictionary.csv new file mode 100644 index 0000000000000..c839b7c46f51c --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/_files/dictionary.csv @@ -0,0 +1,7373 @@ +the +of +and +a +in +to +it +is +to +was +I +for +that +you +he +be +with +on +by +at +have +are +not +this +but +had +they +his +from +she +that +which +or +we +an +were +as +do +been +their +has +would +there +what +will +all +if +can +her +said +who +one +so +up +as +them +some +when +could +him +into +its +then +two +out +time +my +about +did +your +now +me +no +other +only +just +more +these +also +people +know +any +first +see +very +new +may +well +should +like +than +how +get +way +one +our +made +got +after +think +between +many +years +er +those +go +being +because +down +yeah +three +good +back +make +such +on +there +through +year +over +must +still +even +take +too +more +here +own +come +last +does +oh +say +no +going +work +where +erm +us +government +same +man +might +day +yes +however +put +world +over +another +in +want +as +life +most +against +again +never +under +old +much +something +Mr +why +each +while +house +part +number +found +off +different +went +really +thought +came +used +children +always +four +where +without +give +few +within +about +system +local +place +great +during +although +small +before +look +next +when +case +end +things +social +most +find +group +quite +mean +five +party +every +company +women +says +important +took +much +men +information +both +national +often +seen +given +school +fact +money +told +away +high +point +night +state +business +second +British +need +taken +done +right +having +thing +looked +London +area +perhaps +head +water +right +family +long +hand +like +already +possible +nothing +yet +large +left +side +asked +set +whether +days +mm +home +called +John +development +week +use +country +power +later +almost +young +council +himself +far +both +use +room +together +tell +little +political +before +able +become +six +general +service +eyes +members +since +times +problem +anything +market +towards +court +public +others +face +full +doing +war +car +felt +police +keep +held +problems +road +probably +help +interest +available +law +best +form +looking +early +making +today +mother +saw +knew +education +work +actually +policy +ever +so +at least +office +am +research +feel +big +body +door +let +Britain +name +person +services +months +report +question +using +health +turned +lot +million +main +though +words +enough +child +less +book +period +until +several +sure +father +level +control +known +society +major +seemed +around +began +itself +themselves +minister +economic +wanted +upon +areas +after +therefore +woman +England +city +community +only +including +centre +gave +job +among +position +effect +likely +real +clear +staff +black +kind +read +provide +particular +became +line +moment +international +action +special +difficult +certain +particularly +either +open +management +taking +across +idea +further +whole +age +process +act +around +evidence +view +better +off +mind +sense +rather +seems +believe +morning +third +else +half +white +death +sometimes +thus +brought +getting +ten +shall +try +behind +heard +table +change +support +back +sort +Mrs +whose +industry +ago +free +care +order +century +range +European +gone +yesterday +training +working +ask +street +home +word +groups +history +central +all +study +usually +remember +trade +hundred +programme +food +committee +air +hours +experience +rate +hands +indeed +sir +language +land +result +course +someone +everything +certainly +based +team +section +leave +trying +coming +similar +once +minutes +authority +human +changes +little +cases +common +role +Europe +necessary +nature +class +reason +long +saying +town +show +subject +voice +companies +since +simply +especially +department +single +short +personal +pay +value +member +started +run +patients +paper +private +seven +UK +eight +systems +herself +practice +wife +price +type +seem +figure +former +lost +right +need +matter +decision +bank +countries +until +makes +union +terms +financial +needed +south +university +club +president +friend +parents +quality +cos +building +north +stage +meeting +foreign +soon +strong +situation +comes +late +bed +recent +date +low +US +concerned +girl +hard +American +David +according to +twenty +higher +tax +production +various +understand +led +bring +schools +ground +conditions +secretary +weeks +clearly +bad +art +start +include +poor +hospital +friends +decided +shown +music +month +English +tried +game +1990 +May +anyone +wrong +ways +chapter +followed +cost +play +present +love +issue +goes +described +award +Mr. +king +royal +results +workers +April +expected +amount +students +despite +knowledge +June +moved +news +light +March +approach +cut +basis +hair +required +further +paid +series +better +before +field +allowed +easy +kept +questions +natural +live +future +rest +project +greater +feet +meet +simple +died +for +happened +added +manager +computer +security +near +met +evening +means +round +carried +hear +bit +heart +forward +sent +above +attention +labour +story +structure +move +agreed +nine +letter +individual +force +studies +movement +account +per +call +board +success +1989 +French +following +considered +current +everyone +fire +agreement +please +boy +capital +stood +analysis +whatever +population +modern +theory +books +stop +legal +Scotland +material +son +received +model +chance +environment +finally +performance +sea +rights +growth +authorities +provided +nice +whom +produced +relationship +talk +turn +built +final +east +1991 +talking +fine +worked +west +parties +size +record +red +close +property +myself +example +space +giving +normal +nor +reached +buy +serious +quickly +Peter +along +plan +behaviour +France +recently +term +previous +couple +included +pounds +anyway +cup +treatment +energy +total +thank +director +prime +levels +significant +issues +sat +income +top +choice +costs +design +pressure +scheme +July +change +a bit +list +suddenly +continue +technology +hall +takes +ones +details +happy +consider +won +defence +following +parts +loss +industrial +activities +throughout +spent +outside +teachers +generally +opened +floor +round +activity +hope +points +association +nearly +United +allow +rates +sun +army +sorry +wall +hotel +forces +contract +dead +Paul +stay +reported +as well +hour +difference +meant +summer +county +specific +numbers +wide +appropriate +husband +top +played +relations +Dr +figures +chairman +set +lower +product +colour +ideas +George +St +look +arms +obviously +unless +produce +changed +season +developed +unit +appear +investment +test +basic +write +village +reasons +military +original +successful +garden +effects +aware +yourself +exactly +help +suppose +showed +style +employment +passed +appeared +page +hold +suggested +Germany +continued +October +offered +products +popular +science +New +window +expect +beyond +resources +rules +professional +announced +economy +picture +okay +needs +doctor +maybe +events +direct +gives +advice +running +circumstances +sales +risk +interests +September +dark +event +thousand +involved +written +park +1988 +returned +ensure +America +fish +wish +opportunity +commission +1992 +oil +sound +ready +lines +shop +looks +James +immediately +worth +college +press +January +fell +blood +goods +playing +carry +less +film +prices +useful +conference +operation +follows +extent +designed +application +station +television +access +Richard +response +degree +majority +effective +established +wrote +region +green +York +ah +western +traditional +easily +cold +shows +offer +though +statement +Scottish +published +forms +German +down +accept +miles +independent +election +support +importance +lady +site +jobs +needs +plans +earth +earlier +title +parliament +standards +leaving +interesting +houses +planning +considerable +girls +involved +Ireland +increase +species +stopped +concern +public +means +caused +raised +through +glass +physical +thought +Michael +eye +left +heavy +walked +daughter +existing +competition +speak +responsible +up to +river +follow +software +complete +above +November +December +purpose +mouth +medical +responsibility +Sunday +Wales +leader +tomorrow +piece +thirty +lay +officer +task +blue +answer +stand +thinking +extra +highly +places +arm +eventually +campaign +ability +appeal +whole +Charles +skills +opposition +remained +pattern +method +miss +hot +lead +source +bought +baby +lack +once +bill +division +remain +surface +older +charge +methods +trouble +fully +equipment +moving +suggest +disease +officers +past +peace +male +slightly +demand +failed +wants +attempt +types +Christmas +hit +post +policies +hardly +ii +arrived +compared +below +otherwise +windows +West +deal +directly +interested +sale +like +firm +status +happen +box +even if +teacher +radio +provision +variety +show +ran +sector +return +factors +essential +direction +beautiful +civil +base +waiting +caught +sit +develop +character +safety +placed +past +completely +tea +introduced +killed +love +mum +context +fifty +primary +animals +culture +Oxford +brother +obvious +weight +discussion +created +1987 +future +other +start +States +none +sold +let's +machine +afternoon +knows +environmental +fair +William +February +provides +wait +league +trees +positive +organisation +win +condition +families +argument +Saturday +learn +up +normally +claimed +truth +senior +kitchen +works +add +lived +library +minute +believed +enough +transport +share +principle +create +agree +born +players +cash +exchange +rule +budget +turn +pupils +nuclear +sitting +version +English +best +features +duty +annual +balance +front +send +boys +presence +protection +dog +courses +individuals +matters +media +avoid +influence +presented +speaker +stone +relevant +apply +August +explain +deep +Robert +1986 +achieved +slowly +relatively +shares +letters +finished +survey +huge +accepted +covered +review +Smith +closed +form +marriage +commercial +aid +lives +collection +living +speech +Africa +regional +differences +benefit +apparently +effort +gets +executive +later +latter +function +failure +return +chair +reference +horse +becomes +attack +reports +practical +queen +subjects +career +bar +official +text +appears +separate +student +names +sell +holiday +larger +cells +open +progress +early +states +helped +visit +smiled +stock +memory +merely +studio +key +putting +eat +opinion +understanding +regular +decisions +chief +drawn +firms +remains +facilities +values +district +cars +due +mhm +begin +managed +receive +corner +image +edge +sister +politics +expression +instead +impact +quarter +forced +inside +views +scale +plant +race +ball +gold +join +Henry +spend +voice +alone +additional +benefits +1985 +trust +for instance +largely +advantage +associated +increased +standing +dad +foot +somebody +pain +gas +clothes +smaller +aspects +active +affairs +possibly +increase +railway +ended +feeling +network +leaders +nevertheless +cause +half +powerful +step +complex +joined +plants +standard +holding +carefully +length +mind +rise +strength +crime +hard +wind +Mary +possibility +becoming +damage +records +reduce +examples +mainly +credit +winter +impossible +insurance +explained +units +currently +forest +formed +somewhere +earlier +beginning +regarded +fall +confidence +discussed +speed +legislation +mentioned +along +pulled +spoke +debate +intended +bodies +message +middle +plus +supply +100 +skin +Edward +stuff +providing +entirely +front +domestic +require +proved +expressed +treated +match +solution +previously +tonight +patient +actual +difficulties +farm +united +far +build +reach +proposals +extremely +choose +ministers +technical +fresh +ordinary +scene +materials +museum +Thomas +move +article +prevent +achieve +customers +includes +powers +band +items +justice +play +animal +internal +suggests +excellent +face +rich +assessment +save +phone +fairly +football +watched +telephone +steps +decide +South +traffic +watch +coffee +deal +sources +past +buildings +increasingly +relief +distance +introduction +forty +administration +no +safe +applied +sight +Mark +island +potential +banks +housing +meaning +existence +claim +northern +enjoy +reduced +twelve +equally +in front of +walk +very +apart from +watching +cultural +famous +latest +users +TV +cabinet +legs +institutions +Japan +measures +reality +proper +video +worse +lose +argued +train +spirit +programmes +accounts +trial +target +fear +joint +doubt +formal +unemployment +prison +accident +concept +limited +elements +strange +served +papers +discovered +conservative +rock +cover +usual +tree +smile +unable +warm +surely +organization +battle +proportion +difficulty +sides +refused +weekend +construction +picked +distribution +dinner +wine +while +works +obtained +exercise +writing +asking +showing +ahead +rural +lovely +applications +twice +factor +path +games +funds +whereas +nobody +shape +initial +substantial +referred +tend +seat +improve +onto +thanks +aircraft +light +contact +quiet +rain +background +identified +contrast +officials +strategy +average +master +forget +leading +soft +reasonable +seeing +pound +grounds +raise +immediate +communication +client +Paris +star +fourth +suitable +determined +ought +detail +everybody +noted +equal +imagine +appointed +manner +homes +classes +freedom +operations +detailed +keeping +selection +requirements +pair +draw +walls +talks +working +call +danger +attitude +user +overall +offer +female +relationships +Edinburgh +note +afraid +pick +charges +democratic +elections +entered +courts +growing +goal +straight +techniques +sufficient +middle +agency +scientific +eastern +crisis +rose +correct +removed +prince +theatre +Irish +laid +act +expensive +markets +sign +educational +capacity +telling +happens +absolutely +patterns +whilst +managers +purposes +employees +1984 +totally +opportunities +cause +break +will +procedure +feeling +output +mental +frequently +bridge +dangerous +either +fingers +recognition +largest +turning +arrangements +sites +profits +quick +absence +sentence +beside +pass +fields +critical +pointed +prove +listen +inc +recorded +cost +signed +hill +dropped +card +tour +understood +notes +track +1983 +partly +replaced +increased +weather +principles +seriously +familiar +related +package +elsewhere +teaching +bottom +necessarily +commitment +player +double +birds +properly +1993 +Jack +threat +notice +unlikely +admitted +1981 +replied +silence +route +file +liked +supported +issued +perfect +victory +discuss +widely +occur +second +violence +efforts +element +neck +carrying +conflict +pieces +Darlington +under +profit +reaction +colleagues +historical +standard +end +Friday +finance +hope +rooms +projects +closely +fund +daily +below +cell +Liverpool +Tom +southern +expenditure +increasing +discipline +completed +occurred +individual +spring +audience +lead +thousands +grow +conversation +tiny +congress +emphasis +finding +exist +check +alone +consideration +speaking +learning +defined +seek +1979 +appearance +maintain +option +dry +bright +urban +pictures +estate +debt +youth +neither +affected +married +feature +payment +exhibition +liberal +supposed +assembly +reform +empty +boat +suffered +bus +hell +remembered +driver +lunch +flowers +heat +processes +upper +volume +share +captain +murder +North +fifteen +represented +meetings +contribution +drugs +die +feelings +outside +Ian +arts +leg +serve +dealing +writing +curriculum +bag +sought +apparent +branch +beginning +noticed +procedures +models +Martin +enter +revealed +institute +establish +object +occasion +waste +facts +membership +requires +shook +Monday +claims +control +prepared +younger +faith +shops +challenge +answer +Russian +moral +pleasure +orders +Alan +heads +bloody +careful +filled +Corp +literature +birth +1980 +leading +code +centres +broke +prepared +that +professor +1982 +aye +wood +gentleman +flight +entry +pretty +attractive +wild +investigation +crown +protect +nodded +greatest +subject to +functions +encourage +belief +care +developments +description +tradition +Japanese +thin +adopted +vital +document +conclusion +hoped +Italy +enjoyed +engineering +coal +transfer +address +breath +along with +Ltd +alternative +total +schemes +copy +desire +search +effectively +organisations +demands +pushed +visit +etc +planning +farmers +ancient +released +opening +lips +iii +treaty +newspaper +aim +drug +identify +engine +Manchester +USA +tests +owner +sky +Tony +wearing +depends +elderly +ministry +Australia +busy +inside +anybody +reading +external +capable +marketing +streets +partner +respect +shot +institution +generation +acid +realised +chosen +wider +his +narrow +horses +broad +ordered +wonderful +key +contained +laughed +bringing +clients +typical +drink +Stephen +employed +atmosphere +slow +wondered +clean +actions +entire +troops +Leeds +vote +definition +welfare +reduction +row +walking +laws +visitors +release +meanwhile +confirmed +examination +doors +leadership +attitudes +East +enable +beneath +journey +milk +stated +hence +IBM +machines +affect +grey +screen +criticism +surprise +reading +nineteen +stories +billion +constant +teeth +brain +explanation +brief +signs +married +highest +cover +starting +knowing +claim +creation +castle +governments +goals +intention +India +vast +flat +guide +drive +surprised +easier +ideal +shut +readers +run +Bill +magazine +bound +terrible +thoughts +kinds +academic +worry +minor +seats +customer +significance +measure +pleased +unfortunately +o'clock +revolution +attempts +noise +charged +rare +biggest +rather than +somewhat +sections +stared +seeking +paying +meeting +encouraged +thick +Jones +loved +metal +grand +plenty +note +phase +coast +injury +China +granted +motion +observed +technique +ill +drew +potential +factory +lying +severe +mine +lights +wonder +Harry +spread +contains +strongly +offers +afterwards +committed +tape +shoulder +bear +corporate +obtain +kill +that is +worst +learned +settlement +ooh +grew +represent +rapidly +tall +hole +living +adult +iron +amongst +faced +negative +afford +lots +index +permanent +beat +trip +contain +fundamental +doctors +desk +ourselves +sport +unions +implications +fashion +content +similarly +elected +proposed +judge +pool +inflation +brown +Brian +originally +funny +via +practices +somehow +payments +odd +Andrew +pension +pay +crucial +fit +inner +appointment +used +flow +launched +Chris +independence +Spain +objects +setting +little +least +colours +palace +perfectly +combination +contracts +criminal +consequences +pages +contemporary +UN +talked +session +sharp +structures +planned +drive +Wednesday +Kingdom +falling +sample +virtually +fast +sick +movements +dogs +Anne +Yorkshire +Roman +accommodation +nation +temperature +massive +societies +consumer +cities +offices +documents +valley +indicated +breakfast +stayed +kids +display +named +bedroom +sports +aspect +unique +Steve +sixty +author +lane +objectives +secondary +wear +republic +agent +interpretation +assistance +directors +badly +alright +parliamentary +African +Joe +unknown +industries +assets +selling +moreover +Northern +nose +Jim +Russia +subsequent +place +describe +declared +gallery +allowing +ship +other than +visited +cross +grown +crowd +recognised +interview +broken +Simon +argue +BBC +naturally +thinking +general +Mike +meal +catch +representatives +proceedings +tears +alive +involving +shoulders +employers +begun +departments +vision +yours +unix +beauty +guilty +proposal +impression +square +angry +regulations +regions +vehicle +Jane +democracy +sequence +offering +Graham +enormous +invited +cancer +sheet +struck +Glasgow +rarely +involve +involvement +improvement +ninety +motor +shock +tone +significantly +contact +manufacturing +close +seconds +dress +assumed +well +quietly +grass +nations +provisions +communities +roof +yellow +indicate +distinction +present +statements +comments +allows +late +pollution +fruit +acting +involves +unusual +assume +towns +lucky +fuel +spot +properties +touch +fall +Major +bottle +except +anywhere +net +buying +long-term +soil +sum +stages +decline +missed +reader +extensive +manage +calling +talk +heavily +containing +plate +advertising +revenue +remaining +glad +diet +agricultural +artist +plastic +artists +gently +Bob +alright +location +ring +ice +operate +lies +candidates +Italian +pull +passage +principal +cope +linked +tired +periods +firmly +occasionally +identity +persons +limited +warned +efficient +runs +hundreds +maintenance +divided +unlike +establishment +channel +producing +fight +happening +song +map +expert +formation +comfortable +border +constitution +weapons +emergency +Chinese +waited +continues +arranged +link +Wilson +spokesman +extended +rail +Philip +candidate +believes +funding +promised +positions +mostly +household +remove +performed +cat +sleep +abroad +teams +mountain +program +countryside +stars +victim +studied +relative +criteria +conventional +parish +framework +willing +strike +cheap +ref +sudden +approval +concentration +partners +autumn +maintained +warning +cards +roads +approved +lake +starts +determine +liability +editor +realise +thinks +helping +longer +proposed +voluntary +settled +grant +characters +valuable +situations +deputy +walk +regularly +occasions +trading +rejected +agriculture +premises +dramatic +fill +theme +silver +golden +duties +friendly +arguments +accused +driving +losses +error +reflected +dream +shortly +wealth +working +temporary +federal +stress +painting +request +initially +reflect +lifted +eighty +hello +pub +recovery +loan +electricity +1980s +chest +Margaret +refer +taught +silent +Brown +beach +Indian +eleven +answered +learning +recession +focus +facing +video-taped +height +clubs +item +characteristics +emerged +options +matter +hurt +forgotten +worried +bread +admit +chief +specifically +owners +Lewis +statutory +mirror +agents +writer +deeply +Welsh +foundation +struggle +1978 +parent +dependent +mistake +reputation +Frank +eggs +decade +steel +gain +leads +publication +resistance +offence +incident +Thursday +prefer +stations +denied +examine +lifespan +wages +tasks +gained +acquired +outcome +claims +travel +competitive +marked +panel +resolution +wished +dear +efficiency +demanded +flat +yards +subsequently +gradually +businesses +chancellor +chain +specialist +1977 +dressed +tells +negotiations +relating +supporters +armed +radical +sleep +representation +agencies +theories +outside +shoes +threatened +spending +keen +drove +gardens +acceptable +notion +initiative +stairs +Cambridge +advance +leaves +recognise +worker +essentially +empire +shared +sensitive +uses +clause +attached +Taylor +living +fallen +Belfast +fighting +dear +controlled +sugar +Elizabeth +block +global +delivery +changing +Lee +computers +ages +meat +mass +emotional +brothers +bird +expansion +islands +healthy +Middlesbrough +aside +attend +secret +store +1976 +break +writers +self +rising +travel +bigger +alternative +rapid +instructions +wet +adequate +weak +licence +fixed +soldiers +examined +aimed +owned +average +awareness +centuries +images +drama +notice +Tuesday +handed +furniture +gate +scientists +administrative +sees +pocket +wooden +uncle +remarkable +co-operation +creating +Adam +Luke +newspapers +currency +comprehensive +intelligence +charity +fifth +links +hoping +respond +surprising +extension +solid +survive +growing +apart +restaurant +churches +precisely +pale +skill +close +connection +mass +dealt +brilliant +maximum +losing +depend +200 +experienced +across +introduce +philosophy +convention +gun +films +sons +eh +communications +regime +miss +attended +suffer +copies +councils +round +partnership +inquiry +Sarah +residents +absolute +firm +French +corporation +arrested +reports +minority +arrival +in addition to +listening +taste +sad +gap +plane +scope +experiences +coat +command +consequence +left +fun +Birmingham +tory +golf +electronic +behind +visual +retirement +replace +rise +darkness +fault +directed +complete +1970s +enemy +comment +electric +priority +metres +database +Tim +pure +Spanish +Nigel +rough +core +circle +result +literary +bay +championship +guests +1975 +insisted +mere +bits +successfully +limit +imposed +continuing +Rome +sounds +abuse +categories +languages +tower +Thatcher +anger +accompanied +category +collected +present +need +comparison +supreme +supplied +chemical +fans +greatly +sweet +wedding +teaching +represents +duke +mark +personnel +genuine +adults +mail +politicians +occurs +exciting +written +returning +promotion +longer +preparation +defendant +presumably +DNA +derived +Washington +paintings +fitted +mothers +affair +stupid +cricket +advanced +tank +arise +photographs +point +disappeared +expectations +findings +illness +citizens +mood +faces +tension +Commons +ladies +briefly +stones +mixture +classical +arrangement +extreme +Williams +nineteenth +discover +favourite +shot +causing +yard +begins +socialist +judgment +landscape +fail +feels +consumption +mill +1974 +informed +birthday +widespread +consent +confident +acts +Gloucester +sake +estimated +requirement +catholic +experts +Israel +numerous +throat +permission +ignored +guidance +moments +brings +costs +module +opposite +respectively +altogether +input +presentation +everywhere +distinct +statistics +repeated +tough +earnings +saved +finger +branches +fishing +components +truly +drink +turns +luck +boss +exists +champion +MP +answers +tools +cycle +recommended +intervention +mile +Scott +whenever +AIDS +promote +helpful +prospect +definitely +Johnson +organised +controls +Ben +express +Iraq +nurse +frame +perform +cottage +wave +adding +proud +by +winner +solicitor +neighbours +pilot +calls +mad +alliance +given +straight +survival +winning +votes +primarily +attacks +compensation +Sam +destroyed +camp +hat +territory +symptoms +prior to +ownership +wage +concluded +discussions +developing +cream +achievement +drawing +entrance +basically +poverty +disabled +extend +fast +suggestion +consistent +shareholders +degrees +mention +anxious +fewer +delivered +dark +transferred +employee +throw +assumption +inevitably +nervous +profession +awful +cool +hang +threw +vehicles +stable +realized +suit +hills +prize +drop +constitutional +perspective +Neil +satisfied +bid +aunt +festival +constantly +conscious +developing +connected +concerning +savings +reasonably +concentrate +pace +novel +operating +breach +purchase +crossed +Asia +chances +depth +calls +strategic +thrown +bills +Jean +Ken +reply +ha +Ruth +treat +green +sheep +dominant +phrase +push +eating +now that +conducted +employer +ears +contents +touched +prepare +sounded +Moscow +theoretical +setting +soul +wore +study +1960s +outstanding +benefit +vary +jacket +except +holy +processing +sand +clinical +prisoners +dispute +shadow +minimum +organizations +plaintiff +snow +cried +fit +driven +Joseph +port +recognized +servants +limits +pink +et al +sound +hearing +measured +dance +eighteen +sorts +Patrick +trained +stomach +slight +fought +points +storage +breaking +impressive +honest +provided +dismissed +glanced +related +cast +crew +defeat +hold +gift +enthusiasm +princess +press +spending +advantages +reaching +articles +resulted +files +hung +cuts +residential +extraordinary +visible +shouted +reducing +experiments +tables +finish +tendency +conduct +objective +report +ring +hospitals +mechanism +seventy +exception +poetry +inspector +1973 +covering +stepped +accurate +percent +victims +approached +distant +alongside +airport +furthermore +considerably +pressed +missing +origin +salt +personality +fight +Canada +arrive +fly +Greek +receiving +still +rocks +fees +fee +complicated +ends +musical +stands +moon +chamber +puts +turnover +attacked +ultimately +routine +observation +precise +plain +gentle +watch +staring +since +leisure +economics +device +broken +mortgage +live +leaves +confusion +cut +finds +instruments +secondly +Bush +certificate +ear +remote +satisfaction +responsibilities +ratio +coach +fears +sentences +holder +shopping +possession +selected +Bristol +sets +smoke +rugby +songs +clock +summary +implementation +protein +housing +escape +wing +fixed +helps +poll +Arthur +scored +chose +column +holidays +contributions +architecture +Nick +approximately +disaster +minds +Keith +marks +trust +neither +monetary +mountains +White +discovery +collect +spoken +steam +smooth +silly +childhood +teach +Kong +unity +staying +climate +percentage +villages +attracted +Americans +designs +secure +cutting +iv +Hong +last +taxes +tennis +peak +relation +readily +evaluation +frequency +Andy +shirt +cake +nights +bishop +test +pretty +Charlie +deliberately +round +determination +applies +automatically +standing +rent +psychological +violent +medicine +boards +protest +Anna +unemployed +final +being +Rose +reforms +inevitable +junior +signal +building +till +welcome +fat +roles +headquarters +sensible +visits +manufacturers +restrictions +samples +Pacific +improved +Berlin +grateful +strategies +score +tended +expense +loans +addressed +mode +structural +dozen +pride +newly +founded +variation +aged +rely +investors +infection +dominated +combined +survived +Helen +string +Lucy +experiment +fourteen +undertaken +committees +buyer +agreements +participation +welcome +match +complaints +ships +critics +guitar +camera +laboratory +waves +landlord +rang +hate +demonstrated +bomb +engaged +knife +so-called +modules +pleasant +headed +surgery +universities +millions +concepts +proof +marry +Maggie +operating +pressures +lives +sixteen +repeat +host +Dave +pitch +attempted +assess +penalty +tail +boxes +holes +deep +contribute +passing +reveal +cleared +thereby +acceptance +1972 +anxiety +above +Newcastle +formula +personally +Howard +mission +deaf +relatives +imagination +apple +dirty +rid +abandoned +appreciate +continuous +describes +suffering +circuit +stronger +responses +excitement +approaches +supply +plan +Zealand +tested +disk +holds +replacement +instrument +universe +memories +overseas +expertise +causes +solicitors +comfort +sergeant +trend +treasury +entitled +sounds +acquisition +opening +tickets +bath +delighted +sending +increasing +confirm +loose +state +targets +occasional +paragraph +writes +evident +Kent +desperate +handle +fellow +blind +occupied +overcome +dust +burden +psychology +relate +drivers +surprisingly +1970 +Matthew +cheese +consciousness +considering +universal +Gordon +aha +square +succeeded +knees +1971 +boots +smell +closer +mummy +slipped +component +regulation +Roger +attempt +locked +keeps +wheel +classic +MPs +cattle +consists +touch +illustrated +platform +shift +draft +purely +load +influenced +passengers +recommendations +preparing +solutions +Alexander +injuries +tenant +commonly +Victoria +leather +gathered +zone +sufficiently +like +Laura +squad +recall +steady +retain +checked +existed +attract +conservation +flesh +pack +publicity +rose +mean +variations +split +sixth +edition +concerns +tied +summit +engineers +Terry +listed +judges +researchers +equivalent +assist +upstairs +hers +Francis +located +nurses +fear +mark +suggesting +whispered +serving +intellectual +influence +stream +generated +consequently +San +authors +wondering +judgement +experience +Victorian +egg +supplies +level +produces +councillor +roots +taxation +bathroom +ultimate +awarded +stick +glasses +raising +qualities +layer +lost +creative +medieval +risks +assumptions +displayed +dreams +Germans +accounting +curve +drawing +backed +adopt +colleges +guard +evolution +sign +aims +sharply +constructed +advised +softly +settle +decades +completion +linguistic +ignore +convinced +Colin +judicial +photograph +sophisticated +Alice +asleep +paused +amounts +poem +recording +carbon +Durham +possibilities +good +explains +equation +NHS +vulnerable +raw +net +deaths +babies +illegal +outer +topic +medium +till +promise +tends +hopes +angle +interviews +ban +feed +potentially +machinery +tongue +coalition +travelling +define +consultation +reception +pulling +Nicholas +integration +revolutionary +quoted +compare +surrounded +bitter +attempting +guess +improvements +climbed +cathedral +heaven +wanting +painted +Australian +changing +grammar +jumped +Ulster +Gulf +native +imperial +persuade +voices +conclusions +laughing +lift +1968 +Gary +boundaries +favour +guy +studying +Jimmy +beliefs +undoubtedly +wings +retained +joy +bone +informal +demonstrate +Douglas +regard +clear +calculated +flexible +meals +announcement +lawyers +ruled +account +sheets +tunnel +exercise +bars +carpet +quantity +catalogue +reminded +shrugged +notably +Anthony +schedule +petrol +investigate +hotels +buried +once more +journal +nowhere +considerations +concentrated +collective +destruction +frequent +versions +offences +agenda +clever +experimental +plays +Kate +listened +La +texts +plates +deficit +transition +Norman +spiritual +intense +indication +flew +pushing +rational +hanging +entitled +excluded +knocked +professionals +tight +composition +indicates +conservatives +Kevin +interaction +ceiling +guidelines +cold +roughly +Governor +qualifications +ethnic +me +argues +Dublin +inches +opera +pupil +cheaper +generous +prominent +inadequate +accordingly +welcomed +instruction +logical +passion +drawings +exposure +departure +blame +racing +mixed +historic +guest +Mrs. +pipe +modest +Dutch +lessons +hero +Lloyd +sectors +Diana +barely +logic +Essex +acute +harm +representing +discourse +voted +electrical +hearing +consumers +jury +Grant +weekly +acted +delay +valid +wherever +representative +transaction +bowl +increases +contributed +Christopher +record +leaned +lesson +lit +admission +stores +awards +automatic +timber +trousers +vote +habit +Oliver +arrange +red +matches +punishment +bones +cross +deny +rubbish +hide +mortality +complex +pc +earl +explore +urged +occupation +storm +darling +keys +customs +profile +gross +depression +classroom +glance +mystery +mutual +reliable +wholly +entering +bare +liable +facility +stressed +stuck +realize +engineer +smiling +confined +province +registration +males +laughter +humour +resource +multiple +Albert +ruling +silk +waters +Rachel +paint +cotton +Atlantic +identification +claiming +sole +coverage +arising +Owen +honour +poet +prospects +travelled +divisions +posts +avoided +in case +charter +managing +pregnant +obligation +win +adds +formally +flying +Latin +nearby +Egypt +exact +directions +curious +bother +participants +lawyer +resignation +bearing +sets +pointing +tool +damages +speakers +fate +daddy +devices +phenomenon +strain +substance +bags +wire +Wood +underlying +responded +enjoying +visitor +joining +uncertainty +but +drop +submitted +flower +Ford +California +perception +identical +farming +letting +audit +satisfactory +Billy +ticket +lists +preference +Great +thirteen +Van +secret +pop +album +federation +learnt +deliver +Westminster +chemicals +farmer +variables +male +assault +marginal +leave +namely +fed +distinctive +kingdom +assessed +refuse +electoral +urgent +allowance +observations +libraries +Lawrence +reflects +force +sympathy +running +falls +publishing +recovered +stability +canal +funeral +singing +titles +beds +sessions +restricted +Sheffield +Nottingham +expecting +clothing +drinks +disposal +failing +joke +focus +succeed +Maria +typically +official +conversion +presidential +generations +mayor +sharing +Clare +worth +transactions +era +policeman +Fred +gaze +controversial +count +proceed +Young +folk +fabric +oral +horror +Kelly +everyday +emperor +viewed +sing +belt +fortune +demand +doubt +crash +encouraging +interpreted +Louis +organic +maintaining +removal +female +routes +continued +trials +enables +print +laugh +bent +expected +connections +magistrates +errors +statistical +resolved +desirable +recognize +Stuart +thoroughly +injured +van +blocks +prosecution +register +trends +preferred +reckon +innocent +ideology +belong +improved +past +corridor +exclusive +tale +pairs +prayer +collapse +lease +talent +gains +separated +marked +experienced +persuaded +sighed +butter +suggestions +Russell +unexpected +foods +picking +banking +sciences +superb +contacts +operated +alarm +go +Poland +gene +daughters +sheer +guardian +count +cloud +disappointed +Bernard +format +scenes +frightened +hardware +traditionally +gastric +genes +effectiveness +full-time +intend +concentrations +defend +strict +fighting +creatures +closer +Swindon +capitalist +Walker +addition +chocolate +emerge +Hugh +hidden +likes +Susan +Stewart +reactions +lands +establishing +swept +anniversary +permitted +export +1967 +justify +tissue +Davies +bet +specified +romantic +garage +conviction +declined +resigned +Clarke +advise +scientist +root +asset +warmth +bulk +bands +knee +minimum +humans +references +any +associations +muscles +withdrawal +registered +distributed +regarding +exposed +declaration +graphics +reluctant +actor +switched +sisters +winners +eighteenth +chemistry +rest +justified +stop +converted +boundary +suspect +magnificent +stretched +convenient +friendship +established +recover +destroy +Jackson +mess +correspondent +navy +dollar +craft +reflection +chicken +plans +tin +Miller +curtains +gesture +tourist +diary +protected +ocean +discussing +practitioners +bloody +entertainment +nearest +mechanisms +closed +expenses +uncertain +artificial +democrats +damaged +composed +heating +diplomatic +drinking +discrimination +rows +bench +councillors +acquire +installed +guns +killing +Microsoft +blow +salary +Baker +tip +1950s +physically +estates +tremendous +marine +ease +institutional +mechanical +retail +resist +mixed +literally +chapel +distinguish +wildlife +Rivers +Iran +tories +doubts +formerly +priorities +reserves +publications +commented +gender +passenger +Sussex +strictly +boats +causes +pen +chapters +cheque +required +testing +carriage +weapon +generate +Clinton +asks +earn +supporting +mentally +judge +messages +females +biological +applying +implies +known +Emily +rolled +tube +functional +accidents +flexibility +chairs +Phil +styles +cap +straightforward +moves +wise +fired +organized +inspection +Derek +mathematics +heritage +superior +1969 +specially +finance +cloth +sociology +desperately +fiction +equity +satisfy +Lords +shell +Wright +lad +whereby +forests +suit +pursue +digital +increases +tenants +refers +voters +piano +productivity +part-time +lightly +assistant +Commander +address +situated +restoration +outlined +imports +comment +stolen +Harris +clerk +cinema +Ann +covers +capitalism +spectacular +shapes +controversy +Marx +gates +escaped +Robin +continuing +trains +ensuring +colonel +confused +grants +remarks +bonds +wives +computing +constraints +solve +aggressive +availability +unfair +sadly +invasion +tracks +compete +closure +spare +painful +earned +venture +topics +wonder +equivalent +grade +Korea +pot +emotions +washed +escape +abstract +Eric +murmured +stake +lift +states +breeding +securities +asian +mud +Joan +estimates +cheek +stored +correctly +refugees +Moore +obligations +spirits +unhappy +Ross +networks +beaten +snapped +initiatives +understanding +alter +shame +pensions +oxygen +therapy +associated +courage +discretion +dates +deposits +hopefully +exports +legislative +Eliot +ward +monthly +deciding +describing +assuming +opposed +Alex +searching +intelligent +impose +explicit +jurisdiction +designer +tie +fellow +quantities +fleet +Barry +seller +RAF +borough +stand +flats +virtue +constituency +complained +coloured +midnight +taxi +engines +railways +display +just +ridiculous +Caroline +debts +comparable +amazing +acknowledged +appeal +wars +successive +refusal +incorporated +creature +secured +economies +isolation +Leicester +succession +signals +working-class +physics +feared +concert +tonnes +realistic +hungry +launch +Evans +resort +burst +sort +back +Walter +gear +Shakespeare +surveys +volunteers +stick +separation +la +demonstration +fails +conception +decent +discount +unnecessary +prevented +flying +worn +dictionary +twentieth +fat +random +retired +local +origins +packed +achieving +heading +forever +influential +masters +channels +harbour +producers +duration +Thames +cable +1945 +desert +terrace +assured +allocation +check +diseases +merchant +constable +Vietnam +Dean +recalled +lifetime +chips +Ray +genetic +complaint +near +visiting +explaining +order +marvellous +Malcolm +Morgan +restored +earliest +enabled +release +Cardiff +assurance +bottles +brick +essence +autonomy +giant +requiring +hunting +consensus +differ +vegetables +junction +workshop +measure +purchaser +secure +attendance +necessity +bottom +demanding +skilled +shaking +subtle +select +attack +questioned +sooner +producer +planet +elegant +amendment +hopes +carries +recommend +lesser +farms +parallel +limitations +locally +Marie +tragedy +instance +cousin +collections +backwards +grain +resulting +fraud +swung +landed +quarters +liberation +seventeen +referring +interior +bike +suspended +officially +journalists +nasty +movie +suppliers +dealer +shows +soldier +intensive +kit +witness +delight +symbol +forum +casual +tropical +shorter +Allen +crimes +printed +miners +feeding +relax +pass +manufacturer +chip +crazy +forming +kissed +swimming +happily +copper +arguing +shots +landing +nursery +entries +preliminary +besides +arises +partial +households +damp +wool +1964 +servant +Pakistan +attending +Guy +plot +muscle +beings +inch +simultaneously +concrete +Roy +roll +bell +neighbour +reign +analysed +tide +expand +alleged +guilt +rank +introducing +transfer +uses +ceremony +Morris +separately +opinions +enquiry +grinned +lover +slept +choices +assistant +severely +finest +poured +vertical +Easter +upset +hey +allegations +IRA +justification +detective +programs +throwing +strike +ate +appendix +Jenny +districts +commonwealth +dealers +delicate +forms +advisers +lonely +dull +mouse +Pat +occupational +pity +behave +complexity +youngsters +riding +weakness +excessive +Clark +progressive +captured +stance +undertake +exceptional +faster +Iraqi +remind +counter +Greece +triumph +remarked +continental +striking +integrated +pit +encountered +implemented +sizes +directive +participate +safely +lowest +lighting +villa +okay +downstairs +portrait +alternatively +edges +focused +bye +residence +panic +label +aims +magazines +neat +combined +transformation +theft +lecture +incidence +scores +radiation +perceived +spread +firstly +interface +doctrine +shouting +affecting +ours +excuse +accepting +risen +Lancashire +approach +deposit +pond +substantially +innovation +diagnosis +gifts +allocated +regard +remainder +speculation +approaching +dialogue +estimate +wash +supervision +dying +exclusively +happiness +politically +timing +chronic +Geoffrey +peasants +tightly +characteristic +accuracy +compulsory +wrapped +interim +objective +Benjamin +walking +infant +Bruce +judged +splendid +ride +divorce +magic +Cleveland +bond +review +short-term +ambulance +brave +investigations +systematic +Green +seized +cry +laugh +advanced +obliged +opens +eaten +relevance +1930s +careers +Liz +withdrawn +Barbara +no +payable +handsome +fun +Ms +instances +governors +horrible +measurement +employ +primitive +steadily +switch +fascinating +Brazil +ideological +pile +mounted +metropolitan +alternatives +dollars +north-east +explosion +starting +glory +scarcely +Harriet +surrounding +coup +domain +fence +threatening +dragged +breast +habits +mine +hierarchy +grip +socialism +enquiries +particles +Sweden +choosing +colleague +monitoring +Midlands +restore +printer +imagined +doorway +prisoner +juice +classification +estimated +equilibrium +solely +with regard to +serves +peaceful +observer +explanations +circles +rescue +maps +hated +observe +Hughes +premier +mate +hypothesis +1966 +ride +companion +liver +factories +buyers +reward +controlling +satellite +loyalty +operational +pardon +improving +jump +potatoes +intervals +technological +near +fortunately +hostile +advisory +cook +precious +opponents +peasant +insist +geography +button +consistently +cultures +seeds +monopoly +accessible +tournament +moves +excited +determined +owed +pockets +belonged +Hollywood +dining +switch +traditions +compromise +intensity +chaos +obtaining +Mexico +King +combine +altered +nonsense +clouds +themes +suspicion +ranks +disorder +stocks +Kuwait +1965 +2000 +consultant +collapsed +purchased +impressed +half +Catherine +provincial +sterling +performances +instantly +Bell +constitute +arrest +dose +exercises +issue +competitors +spectrum +dangers +allies +travellers +plc +kid +disc +Donald +nowadays +Surrey +cheeks +endless +isolated +dimension +twin +bedrooms +clean +columns +privilege +post-war +volumes +broadcasting +commerce +historians +train +geographical +oak +actors +step +like +dynamic +freely +checking +equipped +inspired +density +1994 +forthcoming +HIV +boring +handled +poems +recording +unfortunate +banned +Karen +own +suspected +boom +tribunal +kicked +possessed +Jonathan +broadly +publicly +attributed +definite +challenged +extending +cooking +pause +strip +predicted +super +barrier +pregnancy +loud +menu +preserved +Avenue +restaurants +acres +prompted +senses +essay +lip +recruitment +defendants +presents +guarantee +invest +cats +maximum +notable +upwards +arose +cry +fierce +detected +indirect +German +witnesses +patch +sensitivity +Le +mistakes +receiver +crops +chin +wheels +rice +Dec +forgot +illustrate +reveals +Freud +limit +chap +Campbell +races +awkward +Turkey +implied +climb +widow +varied +slid +stopping +rope +steep +neutral +Oxfordshire +finish +debut +seed +challenge +promoted +delegation +hitherto +artistic +muttered +adoption +architect +dear +Kenneth +portfolio +continent +transformed +couples +probability +content +Robinson +struggling +mild +counties +wish +mention +fitness +tackle +dish +statute +invariably +Charlotte +prey +view +consultants +gather +arriving +corners +delegates +Holland +archbishop +Sue +withdraw +replacing +Milton +meaning +mature +differently +chart +technologies +woods +possess +cab +grace +toilet +grabbed +prevention +equality +wishes +bases +operator +regardless +harsh +colonial +ambitious +exploration +lords +investigated +collecting +Switzerland +shadows +Corbett +evil +Johnny +dramatically +Marshall +indicating +orchestra +lock +inhabitants +defeated +disappointment +magnetic +washing +fibre +correspondence +verbal +legitimate +requested +emotion +odds +workforce +vessels +brass +pursued +ph +balls +adviser +faint +handling +appointments +grandfather +motivation +sympathetic +publishers +peoples +socially +investments +rhythm +variable +Chelsea +memorial +well-known +empirical +roses +ceased +fluid +descriptions +incidents +DC +dismissal +appreciated +communicate +rushed +bronze +wisdom +Daniel +supper +adventure +tribute +seeks +promise +head +ye +1960 +crop +beef +suited +exercised +respects +terror +circulation +identifying +achievements +fool +intentions +proportions +lads +directory +Brighton +inn +promoting +flag +separate +Roberts +Ward +Dennis +clay +Cook +Norway +attraction +ends +disability +championships +vague +virus +shift +ranging +competence +examining +inform +spaces +goodness +gang +favourite +preserve +remembering +naval +molecules +hearts +trapped +actively +leaf +Brussels +distress +resolve +custody +packages +drinking +operators +myth +gain +voting +Mick +returns +tourists +encouragement +lacking +seldom +processor +sums +integrity +acknowledge +shortage +depressed +rightly +Louise +remarkably +repair +shoot +electronics +wishing +Kinnock +imprisonment +kings +waved +shared +shocked +uniform +added +reject +implement +pays +hesitated +seventh +magic +mid +populations +worthwhile +filling +crystal +fraction +qualified +Newton +Sally +server +Nato +specimens +kiss +reflecting +shower +missing +roll +sword +varieties +clinic +imply +ie +rivals +Julia +breakdown +Anderson +scales +fan +operates +blank +whoever +scandal +oldest +smart +favourable +filter +interviewed +absent +mining +gentlemen +enemies +champions +Duncan +exclusion +boot +locations +Hamilton +transmission +custom +tanks +tries +Gloucestershire +publisher +beating +evidently +Netherlands +Polish +lively +exceptions +Emma +appeals +Israeli +mobility +reviewed +buses +conclude +mix +shore +commissioner +absorbed +Norwich +dawn +developed +guards +incomes +parking +vendor +wishes +republics +loads +barriers +translation +evenings +Hungary +lectures +stimulus +conflicts +remains +margin +question +bothered +neighbourhood +tourism +meanings +FA +desktop +reportedly +risk +zero +demonstrations +dividend +opponent +wake +stiff +rejection +flavour +relates +borrow +emissions +representative +Midland +thereafter +enthusiastic +observers +cited +quid +fortnight +dreadful +guarantee +reduced +rigid +killer +ending +trick +successor +execution +influences +temperatures +mines +drank +coastal +greeted +nightmare +peculiar +corruption +tray +speaks +cupboard +creates +Jordan +Aberdeen +harder +burned +appearing +Swiss +rabbit +environments +comedy +referendum +bureau +avoiding +just about +matrix +honestly +profound +journalist +extended +Julie +tapes +suspension +delayed +eager +comply +selected +skirt +matched +feminist +Davis +Canadian +closing +acts +grief +relaxed +insight +deck +sensation +placing +sequences +temple +parks +tactics +verdict +adapted +enhance +corresponding +strings +accurately +running +pray +accent +envelope +interference +grandmother +examinations +phone +planned +shelf +deemed +waist +waste +onwards +applicable +futures +sauce +immense +purchase +breathing +allied +Norfolk +contest +expects +supports +km +blacks +decision-making +coins +genuinely +accounted +expressing +assessing +dance +scheduled +adjustment +charge +winds +meets +practically +merger +comparative +permit +celebrate +vessel +belonging +affection +outline +albeit +Lily +leaning +lounge +raises +Cheltenham +workshops +refusing +shallow +dishes +monitor +propose +blamed +dioxide +kind +broader +handling +bastard +uncomfortable +affects +proposition +representations +conservation +ya +makers +Yugoslavia +Fox +citizen +forcing +productive +woke +bored +beneficial +slip +campaigns +handful +aged +collar +curtain +diversity +hint +Thompson +disappear +charming +bonus +secrets +interrupted +specialists +accommodate +frustration +recommendation +meantime +coffin +daily +condemned +minimal +mobile +academy +testing +independently +appealed +museums +cruel +faces +murdered +on board +Turkish +aim +Will +territories +pressing +Churchill +commit +verse +research +orange +interval +threats +passive +suspicious +forgive +liberty +ghost +rear +believing +correlation +measurements +1963 +investigating +shade +layers +bias +overwhelming +certainty +Sunderland +cow +commissioned +trusts +maturity +resulting +fatal +surrounding +crying +planted +symbolic +isle +historian +enabling +removing +slope +excuse +angel +nearby +rats +straw +1962 +surfaces +gods +foundations +honours +Belgium +disputes +insects +inspiration +draw +presenting +registered +pavement +telephone +reserve +keeper +dimensions +predict +neighbouring +validity +breeze +ugly +expanded +lasted +irrelevant +complain +shelter +patient +driving +wealthy +upset +hostility +profitable +rod +fled +compact +lamp +shifted +supplier +crossing +phenomena +IT +measuring +horizon +rival +making +clergy +marble +pensioners +fragments +loyal +Alison +Stanley +conscience +sixties +Hill +saving +tune +moderate +1961 +soup +paths +struggled +popularity +score +singer +distinguished +climbing +kick +Betty +characteristic +interior +episode +oven +basket +noble +forwards +consisted +crowds +positively +pole +burning +pet +insufficient +evil +mysterious +jet +eligible +behalf +passes +nails +collaboration +lorry +nest +varying +enforcement +Spencer +Denmark +make-up +molecular +managerial +raid +ambition +middle-class +brand +migration +embassy +neatly +looks +worship +Olympic +devised +exclude +organ +favoured +linear +Samuel +cared +manor +detect +interpret +Kennedy +substances +crude +fantasy +counselling +abilities +treating +blew +embarrassment +executed +implication +Ron +printed +prospective +importantly +Preston +continually +Barnes +executives +catching +forehead +Ali +diverse +parental +elaborate +furious +definitions +appreciation +fiscal +Kim +commitments +sculpture +runs +striker +beans +brush +soccer +spell +reductions +contrary +soap +dated +stretch +publish +russians +pig +stroke +ladder +Greater +burning +expressions +useless +nerve +pence +Gabriel +rumours +relied +Edwards +semantic +inherent +embarrassed +1948 +specification +despair +yep +name +serum +Maxwell +Dick +apartment +Vienna +deliberate +stranger +philosophical +criterion +trap +pubs +utterly +link +frowned +awake +bureaucracy +nonetheless +sunshine +bloke +partially +remedy +battery +variable +within +forth +barn +ties +settlements +installation +crashed +negotiate +Somerset +nursing +dignity +promising +minus +criticised +sacred +analyse +senate +incentive +unpleasant +varied +selective +qualified +Devon +powder +clauses +expectation +tender +inclined +funded +alleged +hidden +ridge +exhibitions +lengths +Joyce +posed +explicitly +symbols +exploitation +receives +1950 +intermediate +Isabel +blocked +trophy +launch +spotted +manufacture +diesel +masses +protective +paint +budgets +Lisa +grows +fortunate +deserve +lap +concerns +varies +compliance +defensive +damage +objections +qualify +featured +suite +salmon +reach +requests +objection +devoted +thesis +repeatedly +blow +palm +Austria +Rover +parked +Carter +Guinness +temporarily +Land +south-east +chains +worthy +ozone +pursuit +valued +divine +react +deals +head +phoned +carrier +jeans +feedback +dancing +tales +rally +grant +performing +rush +handicap +consisting +counted +qualification +guaranteed +negligence +continuity +lend +offers +educated +stuck +surplus +swallowed +eagle +printing +land +Willie +novels +driving +dependence +1st +eighth +Craig +organise +Cornwall +orange +diameter +1939 +toward +auction +eating +Max +invisible +determining +construct +faculty +offenders +occurring +Pete +charm +Don +suffering +contempt +Wimbledon +reinforced +specify +misery +dropping +breasts +overall +Sara +jewellery +bacteria +sin +comparisons +privatisation +owe +squadron +grave +codes +circular +misleading +centred +sunlight +lowered +invested +mathematical +proteins +sanctions +aggression +caution +loch +reply +direct +subjected +inappropriate +diagram +terribly +St +human +liquid +solar +angles +sorted +persistent +poles +laying +inherited +phrases +doubtful +calcium +shake +ingredients +Sophie +admits +black +BR +monster +flames +allowances +sustain +needle +telecommunications +sphere +revenues +guessed +bowel +doubled +prints +rangers +accountants +screaming +legend +petition +predominantly +manual +lies +premium +photo +surroundings +spots +gravel +19th +architectural +bold +Maastricht +inheritance +Harvey +knock +blues +beyond +Day +emergence +beautifully +deeper +intact +cooperation +convince +incredible +sound +devoted +conduct +united +celebration +abruptly +considers +flights +explored +loves +blue +Derby +restriction +prior +submit +gaining +Santa +morality +tragic +musicians +invite +Ipswich +selling +script +coupled +tap +remark +consist +respectable +pint +optimistic +humanity +layout +openly +breed +policemen +Scots +invented +linking +convincing +Harold +guide +vocabulary +Rob +unacceptable +competent +Carrie +spatial +ignoring +applicant +swiftly +easier +painter +decisive +traders +pretend +bargaining +depended +modes +preventing +rage +respective +elite +permanently +seemingly +bunch +carers +fathers +engagement +liquid +Canterbury +binding +fires +sentenced +rebels +founder +ballet +erosion +Gould +syndrome +relieved +nursing +harmony +Coventry +protested +hut +sits +stops +Lamont +bore +instructed +fertility +toxic +testament +1957 +sickness +stretch +Bath +lemon +practise +mix +faster +integral +select +redundant +handle +throne +conceived +polytechnic +nerves +belongs +privately +burn +gravity +labelled +Alfred +bishops +basin +rings +holders +swing +flood +Christie +evolved +sovereignty +then +applicants +cows +lion +Virginia +trail +smoking +trading +Murray +boxing +amateur +probable +scrutiny +tempted +borders +pan +fix +hydrogen +accountability +consulted +echo +sponsorship +fame +El +lakes +protests +patience +documentation +Geoff +backing +search +Mozart +silently +passing +seasons +recipe +fetch +auditors +territorial +specified +abandon +bombs +Los +mineral +horizontal +lined +Robyn +booked +du +cleaning +bear +old-fashioned +inland +youngest +envisaged +floors +thrust +likewise +strengthen +penny +wake +Bradford +overseas +consult +cognitive +Ralph +dock +reaches +disturbed +communists +slim +synthesis +contexts +revival +Reading +regulatory +hurried +defender +dry +miserable +walks +debates +dancing +isolated +venue +Hampshire +resident +rounds +deals +packet +likelihood +remaining +induced +guys +temper +comparatively +calculations +protecting +holdings +corn +1947 +Yeltsin +fusion +Marxist +conferences +creditors +questionnaire +gothic +scared +willingness +civilian +shelves +reporting +precision +divide +Phillips +overnight +Intel +Linda +deputies +Indians +Trevor +Juliet +Watson +conventions +modified +instant +praise +Des +coin +blown +hiding +galleries +1940 +Constance +outlook +incurred +adverse +subsidiary +tiles +seventeenth +Korean +emphasised +Eddie +bile +1959 +fancy +accounting +leaflet +headmaster +crack +heels +truck +engage +reporter +plays +Steven +calm +initiated +brigade +Dorothy +unconscious +convicted +illustration +trustees +sustained +alike +End +ideally +entity +tons +sang +telegraph +negotiation +opposite +smell +aesthetic +wiped +concentrating +anonymous +trace +usage +orthodox +fulfil +polite +girlfriend +lovers +translated +static +intent +cancelled +inside +unaware +presidency +corps +assigned +appearances +exploit +margins +worldwide +cups +solved +panels +halt +EEC +Suffolk +developers +fantastic +Lancaster +seminar +fashionable +criticisms +Cooper +motorway +zones +foolish +intake +advances +receipt +rule +regiment +trades +manual +backs +duck +causal +convey +Tommy +wee +cleaning +fond +compatible +Southampton +inclusion +Herbert +finding +lengthy +two-thirds +tent +shed +implicit +cameras +dare +abolition +Romania +pigs +lace +dedicated +cuts +perceptions +ft +counts +earning +kiss +confirmation +dual +confronted +twenty-five +mistress +assignment +propaganda +toys +Arsenal +Eleanor +critic +curiosity +republican +pipes +reduces +shooting +cheerful +reporting +plea +distinguished +subjective +pie +priests +returns +tel +labels +width +relaxation +advertisement +white +smoke +pencil +legally +following +lacked +surviving +disadvantage +ruling +forward +sleeping +owl +adequately +reproduction +rewards +architects +rear +Shelley +exotic +ambassador +1914 +camps +displays +passages +gazed +timetable +salad +purple +cautious +visiting +Turner +aggregate +ignorance +anticipated +Parker +redundancy +array +penalties +renaissance +theology +try +warn +process +ethical +major +proving +plain +protestant +grid +tenth +takeover +canvas +Ted +skull +highlighted +jokes +beat +pools +twins +borne +criticized +chemical +omitted +revision +sincerely +prizes +salvation +teenage +responding +indicators +repairs +amnesty +comparing +large-scale +yield +Claire +photography +disastrous +thumb +dying +jointly +kilometres +scholars +ace +lump +delicious +confidential +clash +market +underground +Blanche +armed +destination +witnessed +parameters +costly +restraint +bit +1958 +shaped +rode +tips +prosperity +diamond +fury +instinct +reserved +valuation +contacted +subsidies +Hunt +collector +Darwin +sponsored +compound +strengths +sank +defences +lifestyle +prejudice +announce +apparatus +dot +shoe +blanket +wound +Christine +hunger +cabin +photographer +stay +preservation +calendar +assessments +colony +Katherine +thorough +medal +trips +washing +eliminate +breathe +actress +provinces +helicopter +mist +clue +dominance +relaxed +analysts +searched +grin +Czechoslovakia +hitting +inability +portion +restrict +Gray +conspiracy +Nicholson +mercy +log +autonomous +intends +solidarity +jail +genius +1920s +pilots +incorporate +atomic +blade +frozen +1956 +colourful +discharge +injured +mask +provided that +Trent +ease +draws +retire +supposed +ml +angrily +sigh +stamp +adjust +ferry +concessions +majesty +Gilbert +pylori +uniform +adjusted +ashamed +admired +alpha +referee +1944 +Lebanon +respondents +Collins +rested +reconstruction +flown +individually +jaw +submission +efficiently +bitterly +glorious +pour +illustrates +Angeles +amid +convert +wicked +provoked +Chapman +elbow +videos +coherent +annually +le +rising +disciplines +cliff +boyfriend +novel +controls +sweat +depths +Claudia +cave +balanced +strikes +stretching +pains +Close +Tokyo +Portugal +racism +priced +delightful +evaluate +arbitrary +Chicago +Richards +signature +reversed +heroes +clarity +hit +screamed +adjacent +lid +psychiatric +comprising +honey +temptation +beam +immigration +recordings +worrying +weird +practitioner +unchanged +calculation +tutor +politician +rolling +Athens +expedition +electorate +evolutionary +scattered +abolished +researcher +ports +Chester +dilemma +Carl +loaded +IMF +flung +intimate +fever +parallel +tight +miracle +including +lawn +biology +Lothian +failures +breaks +Angela +shy +appraisal +sporting +wines +cleaned +disciplinary +occurrence +smile +formidable +lexical +graduates +fined +cooking +privacy +needles +Reagan +Black +Ed +sink +march +equations +grim +narrative +HP +charts +polls +Paula +express +OK +limbs +decorated +high +addressing +proceeds +pact +Madame +Merseyside +revenge +vice-president +far from +proceeded +airline +minerals +killing +accused +double +gradual +descent +mount +homeless +courtesy +enhanced +supermarket +Blake +Cheshire +interfere +organisers +managing +monitoring +coming +rat +supporting +Marcus +trace +approve +delays +pm +Reynolds +please +yo +programming +training +renewed +Hull +invention +writings +back +excess +planes +legacy +challenges +gaps +dug +Jason +interpretations +smallest +pulse +analyses +Ashley +rubber +retired +specimen +outdoor +shooting +chosen +embarrassing +wrist +atoms +Hereford +smoking +incidentally +preferred +renewal +Japanese +vanished +hook +loudly +bride +Annie +interactions +bizarre +gospel +realm +mainland +knit +appalling +exchanges +surgeon +crews +orientation +twisted +occupy +flame +hatred +exceed +Maurice +laboratories +reviews +Bosnia +agreed +Butler +utility +conversations +imaginative +pursuing +flour +accepted +wartime +governing +Reid +object +cutting +indirectly +governed +palestinian +vocational +von +modification +slopes +allegedly +parade +free +aluminium +Al +movies +biscuits +motive +register +merchants +hip +print +rabbits +remedies +stress +trainer +welcome +wound +Geneva +configuration +boost +puzzled +encounter +axis +no matter how +Clive +worldwide +Arnold +Allan +lamb +laser +vegetation +reluctance +jazz +databases +strengthened +protocol +enjoyment +organisational +knitting +census +calculate +handicapped +mucosa +theirs +1951 +advertisements +eldest +Carol +1936 +eventual +husbands +fur +followers +wasted +pump +lifting +practised +yacht +toes +stimulate +speeches +1953 +traced +ensured +arrow +journals +weekends +spontaneous +appoint +1949 +binding +superintendent +vivid +corporations +organisms +celebrated +mice +motives +torn +tie +dies +1954 +waiting +classified +organs +lack +worlds +nineteenth-century +faithful +shield +withdrew +reckoned +north-west +rolling +missiles +noisy +hire +organising +quote +sofa +reminder +Venice +ministerial +A +daylight +injection +graph +exchanged +prayers +boost +preparations +borrowing +innovative +strongest +audiences +disclosure +confrontation +constitutes +burnt +liaison +armies +strangely +wounds +Hewlett-Packard +controlled +Newman +cease +incentives +extends +Mitchell +echoed +facilitate +resentment +shout +cage +gloves +1990s +exploring +saving +Leonard +crossing +choir +Gibson +exit +Sydney +assumes +woodland +fog +underneath +promises +Ellen +nationalism +Kenya +commentators +Ferguson +metals +reasoning +acids +hunt +pop +1946 +dirt +Texas +Keynes +conceptual +aiming +stating +technically +heading +economically +constituted +Union +maker +blowing +touching +tours +erected +ambitions +spare +chorus +Bond +bladder +settings +dividends +18th +Gaulle +unusually +phases +adapt +colitis +exploded +Nelson +civic +bells +gall +Macdonald +unwilling +retreat +booklet +enforce +defining +goodbye +meaningful +Gregory +pine +borrowed +bow +disturbing \ No newline at end of file diff --git a/setup/src/Magento/Setup/Fixtures/_files/orders_fixture_data.json b/setup/src/Magento/Setup/Fixtures/_files/orders_fixture_data.json new file mode 100644 index 0000000000000..59760c4e5e276 --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/_files/orders_fixture_data.json @@ -0,0 +1,568 @@ +{ + "eav_entity_store": { + "entity_store_id": "'%productStoreId%'", + "entity_type_id": 5, + "store_id": "'%productStoreId%'", + "increment_prefix": "'%productStoreId%'", + "increment_last_id": "'%orderNumber%'", + "_query_suffix": " ON DUPLICATE KEY UPDATE `increment_last_id`='%orderNumber%'" + }, + "sales_order": { + "entity_id": "'%entityId%'", + "state": "'new'", + "status": "'pending'", + "coupon_code": "NULL", + "protect_code": "'272ecb'", + "shipping_description": "'Flat Rate - Fixed'", + "is_virtual": 0, + "store_id": "'%productStoreId%'", + "customer_id": "NULL", + "base_discount_amount": -1.7000, + "base_discount_canceled": "NULL", + "base_discount_invoiced": "NULL", + "base_discount_refunded": "NULL", + "base_grand_total": 25.3000, + "base_shipping_amount": 10.0000, + "base_shipping_canceled": "NULL", + "base_shipping_invoiced": "NULL", + "base_shipping_refunded": "NULL", + "base_shipping_tax_amount": 0.0000, + "base_shipping_tax_refunded": "NULL", + "base_subtotal": 17.0000, + "base_subtotal_canceled": "NULL", + "base_subtotal_invoiced": "NULL", + "base_subtotal_refunded": "NULL", + "base_tax_amount": 0.0000, + "base_tax_canceled": "NULL", + "base_tax_invoiced": "NULL", + "base_tax_refunded": "NULL", + "base_to_global_rate": 1.0000, + "base_to_order_rate": 1.0000, + "base_total_canceled": "NULL", + "base_total_invoiced": "NULL", + "base_total_invoiced_cost": "NULL", + "base_total_offline_refunded": "NULL", + "base_total_online_refunded": "NULL", + "base_total_paid": "NULL", + "base_total_qty_ordered": "NULL", + "base_total_refunded": "NULL", + "discount_amount": -1.7000, + "discount_canceled": "NULL", + "discount_invoiced": "NULL", + "discount_refunded": "NULL", + "grand_total": 25.3000, + "shipping_amount": 10.0000, + "shipping_canceled": "NULL", + "shipping_invoiced": "NULL", + "shipping_refunded": "NULL", + "shipping_tax_amount": 0.0000, + "shipping_tax_refunded": "NULL", + "store_to_base_rate": 0.0000, + "store_to_order_rate": 0.0000, + "subtotal": 17.0000, + "subtotal_canceled": "NULL", + "subtotal_invoiced": "NULL", + "subtotal_refunded": "NULL", + "tax_amount": 0.0000, + "tax_canceled": "NULL", + "tax_invoiced": "NULL", + "tax_refunded": "NULL", + "total_canceled": "NULL", + "total_invoiced": "NULL", + "total_offline_refunded": "NULL", + "total_online_refunded": "NULL", + "total_paid": "NULL", + "total_qty_ordered": 2.0000, + "total_refunded": "NULL", + "can_ship_partially": "NULL", + "can_ship_partially_item": "NULL", + "customer_is_guest": 1, + "customer_note_notify": 1, + "billing_address_id": 2, + "customer_group_id": 0, + "edit_increment": "NULL", + "email_sent": 1, + "send_email": 1, + "forced_shipment_with_invoice": "NULL", + "payment_auth_expiration": "NULL", + "quote_address_id": "NULL", + "quote_id": "'%entityId%'", + "shipping_address_id": 1, + "adjustment_negative": "NULL", + "adjustment_positive": "NULL", + "base_adjustment_negative": "NULL", + "base_adjustment_positive": "NULL", + "base_shipping_discount_amount": "NULL", + "base_subtotal_incl_tax": 17.0000, + "base_total_due": 25.3000, + "payment_authorization_amount": "NULL", + "shipping_discount_amount": "NULL", + "subtotal_incl_tax": 17.0000, + "total_due": 25.3000, + "weight": 2.0000, + "customer_dob": "NULL", + "increment_id": "'%orderNumber%'", + "applied_rule_ids": 1, + "base_currency_code": "'USD'", + "customer_email": "'%email%'", + "customer_firstname": "NULL", + "customer_lastname": "NULL", + "customer_middlename": "NULL", + "customer_prefix": "NULL", + "customer_suffix": "NULL", + "customer_taxvat": "NULL", + "discount_description": "NULL", + "ext_customer_id": "NULL", + "ext_order_id": "NULL", + "global_currency_code": "'USD'", + "hold_before_state": "NULL", + "hold_before_status": "NULL", + "order_currency_code": "'USD'", + "original_increment_id": "NULL", + "relation_child_id": "NULL", + "relation_child_real_id": "NULL", + "relation_parent_id": "NULL", + "relation_parent_real_id": "NULL", + "remote_ip": "'127.0.0.1'", + "shipping_method": "'flatrate_flatrate'", + "store_currency_code": "'USD'", + "store_name": "'%productStoreName%'", + "x_forwarded_for": "NULL", + "customer_note": "NULL", + "created_at": "'%time%'", + "updated_at": "'%time%'", + "total_item_count": "'%itemsPerOrder%'", + "customer_gender": "NULL", + "discount_tax_compensation_amount": 0.0000, + "base_discount_tax_compensation_amount": 0.0000, + "shipping_discount_tax_compensation_amount": 0.0000, + "base_shipping_discount_tax_compensation_amnt": "NULL", + "discount_tax_compensation_invoiced": "NULL", + "base_discount_tax_compensation_invoiced": "NULL", + "discount_tax_compensation_refunded": "NULL", + "base_discount_tax_compensation_refunded": "NULL", + "shipping_incl_tax": 10.0000, + "base_shipping_incl_tax": 10.0000, + "coupon_rule_name": "NULL", + "gift_message_id": "NULL" + }, + "sales_order_address": { + "entity_id": "'%orderAddressId%'", + "parent_id": "'%entityId%'", + "customer_address_id": "NULL", + "quote_address_id": "NULL", + "region_id": "1", + "customer_id": "NULL", + "fax": "NULL", + "region": "'%state%'", + "postcode": "'%zip%'", + "lastname": "'%lastName%'", + "street": "'%address%'", + "city": "'%city%'", + "email": "'%email%'", + "telephone": "'%phone%'", + "country_id": "'%country%'", + "firstname": "'%firstName%'", + "address_type": "'%addressType%'", + "prefix": "NULL", + "middlename": "NULL", + "suffix": "NULL", + "company": "'%company%'", + "vat_id": "NULL", + "vat_is_valid": "NULL", + "vat_request_id": "NULL", + "vat_request_date": "NULL", + "vat_request_success": "NULL" + }, + "sales_order_grid": { + "entity_id": "'%entityId%'", + "status": "'pending'", + "store_id": "'%productStoreId%'", + "store_name": "'%entityId%'", + "customer_id": "NULL", + "base_grand_total": 25.3000, + "base_total_paid": "NULL", + "grand_total": 25.3000, + "total_paid": "NULL", + "increment_id": "'%orderNumber%'", + "base_currency_code": "'USD'", + "order_currency_code": "'USD'", + "shipping_name": "''", + "billing_name": "''", + "created_at": "'%time%'", + "updated_at": "NULL" + }, + "sales_order_item": { + "item_id": "'%itemId%'", + "order_id": "'%entityId%'", + "parent_item_id": "'%parentItemId%'", + "quote_item_id": "'%itemId%'", + "store_id": "'%productStoreId%'", + "created_at": "'%time%'", + "updated_at": "'0000-00-00 00:00:00'", + "product_id": "'%productId%'", + "product_type": "'%productType%'", + "product_options": "'%productOptions%'", + "weight": 1.0000, + "is_virtual": "'0'", + "sku": "'%sku%'", + "name": "'%name%'", + "description": "NULL", + "applied_rule_ids": "'1'", + "additional_data": "NULL", + "is_qty_decimal": "'0'", + "no_discount": "'0'", + "qty_backordered": "NULL", + "qty_canceled": 0.0000, + "qty_invoiced": 0.0000, + "qty_ordered": 1.0000, + "qty_refunded": 0.0000, + "qty_shipped": 0.0000, + "base_cost": "NULL", + "price": 8.5000, + "base_price": 8.5000, + "original_price": 10.0000, + "base_original_price": 10.0000, + "tax_percent": 0.0000, + "tax_amount": 0.0000, + "base_tax_amount": 0.0000, + "tax_invoiced": 0.0000, + "base_tax_invoiced": 0.0000, + "discount_percent": 10.0000, + "discount_amount": 0.8500, + "base_discount_amount": 0.8500, + "discount_invoiced": 0.0000, + "base_discount_invoiced": 0.0000, + "amount_refunded": 0.0000, + "base_amount_refunded": 0.0000, + "row_total": 8.5000, + "base_row_total": 8.5000, + "row_invoiced": 0.0000, + "base_row_invoiced": 0.0000, + "row_weight": 1.0000, + "base_tax_before_discount": "NULL", + "tax_before_discount": "NULL", + "ext_order_item_id": "NULL", + "locked_do_invoice": "NULL", + "locked_do_ship": "NULL", + "price_incl_tax": 8.5000, + "base_price_incl_tax": 8.5000, + "row_total_incl_tax": 8.5000, + "base_row_total_incl_tax": 8.5000, + "discount_tax_compensation_amount": 0.0000, + "base_discount_tax_compensation_amount": 0.0000, + "discount_tax_compensation_invoiced": "NULL", + "base_discount_tax_compensation_invoiced": "NULL", + "discount_tax_compensation_refunded": "NULL", + "base_discount_tax_compensation_refunded": "NULL", + "tax_canceled": "NULL", + "discount_tax_compensation_canceled": "NULL", + "tax_refunded": "NULL", + "base_tax_refunded": "NULL", + "discount_refunded": "NULL", + "base_discount_refunded": "NULL", + "free_shipping": "'0'", + "gift_message_id": "NULL", + "gift_message_available": "NULL", + "weee_tax_applied": "NULL", + "weee_tax_applied_amount": "NULL", + "weee_tax_applied_row_amount": "NULL", + "weee_tax_disposition": "NULL", + "weee_tax_row_disposition": "NULL", + "base_weee_tax_applied_amount": "NULL", + "base_weee_tax_applied_row_amnt": "NULL", + "base_weee_tax_disposition": "NULL", + "base_weee_tax_row_disposition": "NULL" + }, + "sales_order_payment": { + "entity_id": "'%entityId%'", + "parent_id": "'%entityId%'", + "base_shipping_captured": "NULL", + "shipping_captured": "NULL", + "amount_refunded": "NULL", + "base_amount_paid": "NULL", + "amount_canceled": "NULL", + "base_amount_authorized": "NULL", + "base_amount_paid_online": "NULL", + "base_amount_refunded_online": "NULL", + "base_shipping_amount": 10.0000, + "shipping_amount": 10.0000, + "amount_paid": "NULL", + "amount_authorized": "NULL", + "base_amount_ordered": 25.3000, + "base_shipping_refunded": "NULL", + "shipping_refunded": "NULL", + "base_amount_refunded": "NULL", + "amount_ordered": 25.3000, + "base_amount_canceled": "NULL", + "quote_payment_id": "NULL", + "additional_data": "NULL", + "cc_exp_month": "NULL", + "cc_ss_start_year": "'0'", + "echeck_bank_name": "NULL", + "method": "'checkmo'", + "cc_debug_request_body": "NULL", + "cc_secure_verify": "NULL", + "protection_eligibility": "NULL", + "cc_approval": "NULL", + "cc_last_4": "NULL", + "cc_status_description": "NULL", + "echeck_type": "NULL", + "cc_debug_response_serialized": "NULL", + "cc_ss_start_month": "'0'", + "echeck_account_type": "NULL", + "last_trans_id": "NULL", + "cc_cid_status": "NULL", + "cc_owner": "NULL", + "cc_type": "NULL", + "po_number": "NULL", + "cc_exp_year": "NULL", + "cc_status": "NULL", + "echeck_routing_number": "NULL", + "account_status": "NULL", + "anet_trans_method": "NULL", + "cc_debug_response_body": "NULL", + "cc_ss_issue": "NULL", + "echeck_account_name": "NULL", + "cc_avs_status": "NULL", + "cc_number_enc": "NULL", + "cc_trans_id": "NULL", + "address_status": "NULL", + "additional_information": "'a:1:{s:53:\"a:1:{s:12:\"method_title\";s:19:\"Check \/ Money order\";}\";N;}'" + }, + "sales_order_status_history": { + "entity_id": "'%entityId%'", + "parent_id": "'%entityId%'", + "is_customer_notified": "1", + "is_visible_on_front": "0", + "comment": "NULL", + "status": "'pending'", + "created_at": "'%time%'", + "entity_name": "'order'" + }, + "quote": { + "entity_id": "'%entityId%'", + "store_id": "'%productStoreId%'", + "created_at": "'%time%'", + "updated_at": "'1970-01-01 03:00:00'", + "converted_at": "NULL", + "is_active": 0, + "is_virtual": 0, + "is_multi_shipping": 0, + "items_count": "'%itemsPerOrder%'", + "items_qty": "'%itemsPerOrder%'", + "orig_order_id": 0, + "store_to_base_rate": 0.0000, + "store_to_quote_rate": 0.0000, + "base_currency_code": "'USD'", + "store_currency_code": "'USD'", + "quote_currency_code": "'USD'", + "grand_total": 25.3000, + "base_grand_total": 25.3000, + "checkout_method": "'guest'", + "customer_id": "NULL", + "customer_tax_class_id": 3, + "customer_group_id": 0, + "customer_email": "'%email%'", + "customer_prefix": "NULL", + "customer_firstname": "NULL", + "customer_middlename": "NULL", + "customer_lastname": "NULL", + "customer_suffix": "NULL", + "customer_dob": "NULL", + "customer_note": "NULL", + "customer_note_notify": 1, + "customer_is_guest": 1, + "remote_ip": "'127.0.0.1'", + "applied_rule_ids": "'1'", + "reserved_order_id": "NULL", + "password_hash": "NULL", + "coupon_code": "NULL", + "global_currency_code": "'USD'", + "base_to_global_rate": 1.0000, + "base_to_quote_rate": 1.0000, + "customer_taxvat": "NULL", + "customer_gender": "NULL", + "subtotal": 17.0000, + "base_subtotal": 17.0000, + "subtotal_with_discount": 15.3000, + "base_subtotal_with_discount": 15.3000, + "is_changed": 1, + "trigger_recollect": 0, + "ext_shipping_info": "NULL", + "is_persistent": 0, + "gift_message_id": "NULL", + "_table": "quote", + "_resource": "Magento\\Quote\\Model\\ResourceModel\\Quote" + }, + "quote_address": { + "address_id": "'%orderAddressId%'", + "quote_id": "'%entityId%'", + "created_at": "'%time%'", + "updated_at": "'1970-01-01 03:00:00'", + "customer_id": "NULL", + "save_in_address_book": 1, + "customer_address_id": "NULL", + "address_type": "'%addressType%'", + "email": "'%email%'", + "prefix": "NULL", + "firstname": "'%firstName%'", + "middlename": "NULL", + "lastname": "'%lastName%'", + "suffix": "NULL", + "company": "'%company%'", + "street": "'%address%'", + "city": "'%city%'", + "region": "'%state%'", + "region_id": "1", + "postcode": "'%zip%'", + "country_id": "'%country%'", + "telephone": "'%phone%'", + "fax": "NULL", + "same_as_billing": 0, + "collect_shipping_rates": 0, + "shipping_method": "'flatrate_flatrate'", + "shipping_description": "'Flat Rate - Fixed'", + "weight": 0.0000, + "subtotal": 0.0000, + "base_subtotal": 0.0000, + "subtotal_with_discount": 0.0000, + "base_subtotal_with_discount": 0.0000, + "tax_amount": 0.0000, + "base_tax_amount": 0.0000, + "shipping_amount": 0.0000, + "base_shipping_amount": 0.0000, + "shipping_tax_amount": "NULL", + "base_shipping_tax_amount": "NULL", + "discount_amount": 0.0000, + "base_discount_amount": 0.0000, + "grand_total": 0.0000, + "base_grand_total": 0.0000, + "customer_notes": "NULL", + "applied_taxes": "NULL", + "discount_description": "NULL", + "shipping_discount_amount": "NULL", + "base_shipping_discount_amount": "NULL", + "subtotal_incl_tax": 0.0000, + "base_subtotal_total_incl_tax": "NULL", + "discount_tax_compensation_amount": 0.0000, + "base_discount_tax_compensation_amount": 0.0000, + "shipping_discount_tax_compensation_amount": 0.0000, + "base_shipping_discount_tax_compensation_amnt": "NULL", + "shipping_incl_tax": "NULL", + "base_shipping_incl_tax": "NULL", + "free_shipping": 0, + "vat_id": "NULL", + "vat_is_valid": "NULL", + "vat_request_id": "NULL", + "vat_request_date": "NULL", + "vat_request_success": "NULL", + "gift_message_id": "NULL", + "_table": "quote_address", + "_resource": "Magento\\Quote\\Model\\ResourceModel\\Quote\\Address" + }, + "quote_payment": { + "payment_id": "'%entityId%'", + "quote_id": "'%entityId%'", + "created_at": "'%time%'", + "updated_at": "'1970-01-01 03:00:00'", + "method": "'checkmo'", + "cc_type": "NULL", + "cc_number_enc": "NULL", + "cc_last_4": "NULL", + "cc_cid_enc": "NULL", + "cc_owner": "NULL", + "cc_exp_month": "NULL", + "cc_exp_year": "0", + "cc_ss_owner": "NULL", + "cc_ss_start_month": "0", + "cc_ss_start_year": "0", + "po_number": "NULL", + "additional_data": "NULL", + "cc_ss_issue": "NULL", + "additional_information": "NULL", + "_table": "quote_payment", + "_resource": "Magento\\Quote\\Model\\ResourceModel\\Quote\\Payment" + }, + "quote_shipping_rate": { + "rate_id": "'%orderAddressId%'", + "address_id": "'%orderAddressId%'", + "created_at": "'%time%'", + "updated_at": "'1970-01-01 03:00:00'", + "carrier": "'flatrate'", + "carrier_title": "'Flat Rate'", + "code": "'flatrate_flatrate'", + "method": "'flatrate'", + "method_description": "NULL", + "price": "10.0000", + "error_message": "NULL", + "method_title": "'Fixed'", + "_table": "quote_shipping_rate", + "_resource": "Magento\\Quote\\Model\\ResourceModel\\Quote\\Address\\Rate" + }, + "quote_item": { + "item_id": "'%itemId%'", + "quote_id": "'%entityId%'", + "created_at": "'1970-01-01 03:00:00'", + "updated_at": "'1970-01-01 03:00:00'", + "product_id": "'%productId%'", + "store_id": "'%productStoreId%'", + "parent_item_id": "%parentItemId%", + "is_virtual": "0", + "sku": "'%sku%'", + "name": "'%name%'", + "description": "NULL", + "applied_rule_ids": "'1'", + "additional_data": "NULL", + "is_qty_decimal": "0", + "no_discount": "0", + "weight": "1.0000", + "qty": "1.0000", + "price": "8.5000", + "base_price": "8.5000", + "custom_price": "NULL", + "discount_percent": "10.0000", + "discount_amount": "0.8500", + "base_discount_amount": "0.8500", + "tax_percent": "0.0000", + "tax_amount": "0.0000", + "base_tax_amount": "0.0000", + "row_total": "8.5000", + "base_row_total": "8.5000", + "row_total_with_discount": "0.0000", + "row_weight": "1.0000", + "product_type": "'simple'", + "base_tax_before_discount": "NULL", + "tax_before_discount": "NULL", + "original_custom_price": "NULL", + "redirect_url": "NULL", + "base_cost": "NULL", + "price_incl_tax": "8.5000", + "base_price_incl_tax": "8.5000", + "row_total_incl_tax": "8.5000", + "base_row_total_incl_tax": "8.5000", + "discount_tax_compensation_amount": "0.0000", + "base_discount_tax_compensation_amount": "0.0000", + "free_shipping": "0", + "gift_message_id": "NULL", + "weee_tax_applied": "NULL", + "weee_tax_applied_amount": "NULL", + "weee_tax_applied_row_amount": "NULL", + "weee_tax_disposition": "NULL", + "weee_tax_row_disposition": "NULL", + "base_weee_tax_applied_amount": "NULL", + "base_weee_tax_applied_row_amnt": "NULL", + "base_weee_tax_disposition": "NULL", + "base_weee_tax_row_disposition": "NULL", + "_table": "quote_item", + "_resource": "Magento\\Quote\\Model\\ResourceModel\\Quote\\Item" + }, + "quote_item_option": { + "item_id": "'%itemId%'", + "product_id": "'%productId%'", + "code": "'%code%'", + "value": "'%value%'", + "_table": "quote_item_option", + "_resource": "Magento\\Quote\\Model\\ResourceModel\\Quote\\Item\\Option" + } +} diff --git a/setup/src/Magento/Setup/Fixtures/tax_rates.csv b/setup/src/Magento/Setup/Fixtures/_files/tax_rates.csv similarity index 100% rename from setup/src/Magento/Setup/Fixtures/tax_rates.csv rename to setup/src/Magento/Setup/Fixtures/_files/tax_rates.csv diff --git a/setup/src/Magento/Setup/Fixtures/_files/tax_rates_small.csv b/setup/src/Magento/Setup/Fixtures/_files/tax_rates_small.csv new file mode 100644 index 0000000000000..bc68322ec9e78 --- /dev/null +++ b/setup/src/Magento/Setup/Fixtures/_files/tax_rates_small.csv @@ -0,0 +1,133 @@ +"Code","Country","State","Zip/Post Code","Rate","Zip/Post is Range","Range From","Range To","default" +"US-AL","US","AL","*","4","","","","" +"US-AK","US","AK","*","0","","","","" +"US-AZ","US","AZ","*","6.6","","","","" +"US-AR","US","AR","*","6","","","","" +"US-CA","US","CA","*","8.25","","","","" +"US-CO","US","CO","*","2.9","","","","" +"US-CT","US","CT","*","6","","","","" +"US-DE","US","DE","*","0","","","","" +"US-FL","US","FL","*","6","","","","" +"US-GA","US","GA","*","4","","","","" +"US-HI","US","HI","*","4","","","","" +"US-ID","US","ID","*","6","","","","" +"US-IL","US","IL","*","6.25","","","","" +"US-IN","US","IN","*","7","","","","" +"US-IA","US","IA","*","6","","","","" +"US-KS","US","KS","*","6.3","","","","" +"US-KY","US","KY","*","6","","","","" +"US-LA","US","LA","*","4","","","","" +"US-ME","US","ME","*","5","","","","" +"US-MD","US","MD","*","6","","","","" +"US-MA","US","MA","*","6.25","","","","" +"US-MI","US","MI","*","6","","","","" +"US-MN","US","MN","*","6.875","","","","" +"US-MS","US","MS","*","7","","","","" +"US-MO","US","MO","*","4.225","","","","" +"US-MT","US","MT","*","0","","","","" +"US-NE","US","NE","*","5.5","","","","" +"US-NV","US","NV","*","6.85","","","","" +"US-NH","US","NH","*","0","","","","" +"US-NJ","US","NJ","*","7","","","","" +"US-NM","US","NM","*","5.125","","","","" +"US-NY","US","NY","*","4","","","","" +"US-NC","US","NC","*","5.75","","","","" +"US-ND","US","ND","*","5","","","","" +"US-OH","US","OH","*","5.5","","","","" +"US-OK","US","OK","*","4.5","","","","" +"US-OR","US","OR","*","0","","","","" +"US-PA","US","PA","*","6","","","","" +"US-RI","US","RI","*","7","","","","" +"US-SC","US","SC","*","6","","","","" +"US-SD","US","SD","*","4","","","","" +"US-TN","US","TN","*","7","","","","" +"US-TX","US","TX","*","6.25","","","","" +"US-UT","US","UT","*","4.7","","","","" +"US-VT","US","VT","*","6","","","","" +"US-VA","US","VA","*","4","","","","" +"US-WA","US","WA","*","6.5","","","","" +"US-WV","US","WV","*","6","","","","" +"US-WI","US","WI","*","5","","","","" +"US-WY","US","WY","*","4","","","","" +"Austria (standard)","AT","*","","20.0000","","","","" +"Austria (reduced: food / books / pharma / hotels)","AT","*","","10.0000","","","","" +"Belgium (standard)","BE","*","","21.0000","","","","" +"Belgium (reduced: restaurants)","BE","*","","12.0000","","","","" +"Belgium (reduced: food / books/ pharma / medical / hotels)","BE","*","","6.0000","","","","" +"Bulgaria (standard)","BG","*","","20.0000","","","","" +"Bulgaria (reduced: hotels)","BG","*","","9.0000","","","","" +"Croatia (standard)","HR","*","","25.0000","","","","" +"Croatia (reduced: hotels / newspapers)","HR","*","","13.0000","","","","" +"Cyprus (standard)","CY","*","","19.0000","","","","" +"Cyprus (reduced: hotels)","CY","*","","9.0000","","","","" +"Cyprus (reduced: food / books / pharma / medical)","CY","*","","5.0000","","","","" +"Czech Republic (standard)","CZ","*","","21.0000","","","","" +"Czech Republic (reduced: food / medical / pharma / hotels)","CZ","*","","15.0000","","","","" +"Czech Republic (reduced: pharma / books)","CZ","*","","10.0000","","","","" +"Denmark (standard)","DK","*","","25.0000","","","","" +"Estonia (standard)","EE","*","","20.0000","","","","" +"Estonia (reduced: books / pharma / medical / hotels)","EE","*","","9.0000","","","","" +"Finland (standard)","FI","*","","24.0000","","","","" +"Finland (reduced: restaurants)","FI","*","","14.0000","","","","" +"Finland (reduced: books / pharma / hotels)","FI","*","","10.0000","","","","" +"France (standard)","FR","*","","20.0000","","","","" +"France (reduced: pharma / hotels / restaurants)","FR","*","","10.0000","","","","" +"France (reduced: medicals / food / books)","FR","*","","5.5000","","","","" +"France (reduced: newspapers / pharma)","FR","*","","2.1000","","","","" +"Germany (standard)","DE","*","","19.0000","","","","" +"Germany (reduced: food / medical / books / hotels)","DE","*","","7.0000","","","","" +"Greece (standard)","GR","*","","24.0000","","","","" +"Greece (reduced: food / pharma / medical)","GR","*","","13.0000","","","","" +"Greece (reduced: books / hotels)","GR","*","","6.0000","","","","" +"Hungary (standard)","HU","*","","27.0000","","","","" +"Hungary (reduced: food / hotels)","HU","*","","18.0000","","","","" +"Hungary (reduced: books / medical)","HU","*","","5.0000","","","","" +"Ireland (standard)","IE","*","","23.0000","","","","" +"Ireland (reduced: medical)","IE","*","","13.5000","","","","" +"Ireland (reduced: newspapers / hotels / restaurants)","IE","*","","9.0000","","","","" +"Ireland (reduced: food)","IE","*","","4.8000","","","","" +"Ireland (free: books / medical / children clothing)","IE","*","","0.0000","","","","" +"Italy (standard)","IT","*","","22.0000","","","","" +"Italy (reduced: transport / hotels / restaurants)","IT","*","","10.0000","","","","" +"Italy (reduced: food / medical / books)","IT","*","","4.0000","","","","" +"Latvia (standard)","LV","*","","21.0000","","","","" +"Latvia (reduced: books / pharma / medical / hotels)","LV","*","","12.0000","","","","" +"Lithuania (standard)","LT","*","","21.0000","","","","" +"Lithuania (reduced: books)","LT","*","","9.0000","","","","" +"Lithuania (reduced: medical)","LT","*","","5.0000","","","","" +"Luxembourg (standard)","LU","*","","17.0000","","","","" +"Luxembourg (reduced: wine / advertising)","LU","*","","14.0000","","","","" +"Luxembourg (reduced: bikes / domestic)","LU","*","","8.0000","","","","" +"Luxembourg (reduced: food / books / pharma / medical / hotels / restaurants)","LU","*","","3.0000","","","","" +"Malta (standard)","MT","*","","18.0000","","","","" +"Malta (reduced: hotels)","MT","*","","7.0000","","","","" +"Malta (reduced: books / medical)","MT","*","","5.0000","","","","" +"Malta (reduced: food / pharma)","MT","*","","0.0000","","","","" +"Netherlands (standard)","NL","*","","21.0000","","","","" +"Netherlands (reduced: food / books / pharma / medical / hotels)","NL","*","","6.0000","","","","" +"Poland (standard)","PL","*","","23.0000","","","","" +"Poland (reduced: pharma / medical / hotels / restaurants)","PL","*","","8.0000","","","","" +"Poland (reduced: food)","PL","*","","5.0000","","","","" +"Portugal (standard)","PT","*","","23.0000","","","","" +"Portugal (reduced: food)","PT","*","","13.0000","","","","" +"Portugal (reduced: food / books / pharma / medical / hotels)","PT","*","","6.0000","","","","" +"Romania (standard)","RO","*","","24.0000","","","","" +"Romania (reduced: social housing)","RO","*","","5.0000","","","","" +"Romania (reduced: books / pharma / medical / hotels)","RO","*","","9.0000","","","","" +"Slovakia (standard)","SK","*","","20.0000","","","","" +"Slovakia (reduced: books / food / medical / pharma)","SK","*","","10.0000","","","","" +"Slovenia (standard)","SI","*","","22.0000","","","","" +"Slovenia (reduced: food / books / pharma / medical / hotels)","SI","*","","9.5000","","","","" +"Spain (standard)","ES","*","","21.0000","","","","" +"Spain (reduced: medical / pharma)","ES","*","","10.0000","","","","" +"Spain (reduced: food / newspapers)","ES","*","","4.0000","","","","" +"Spain (Tenerife) (standard)","ES","Santa Cruz de Tenerife","","0.0000","","","","" +"Spain (Las Palmas) (standard)","ES","Las Palmas","*","0.0000","","","","" +"Spain (Melilla) (standard)","ES","Melilla","","0.0000","","","","" +"Spain (Ceuta) (standard)","ES","Ceuta","*","0.0000","","","","" +"Sweden (standard)","SE","*","","25.0000","","","","" +"Sweden (reduced: food)","SE","*","","12.0000","","","","" +"Sweden (reduced: books)","SE","*","","6.0000","","","","" +"United Kingdom (standard)","GB","*","","20.0000","","","","" +"United Kingdom (reduced: property renovations)","GB","*","","5.0000","","","","" +"United Kingdom (reduced: food / books / pharma / medical)","GB","*","","0.0000","","","","" \ No newline at end of file diff --git a/setup/src/Magento/Setup/Model/Address/AddressDataGenerator.php b/setup/src/Magento/Setup/Model/Address/AddressDataGenerator.php new file mode 100644 index 0000000000000..683674396cfa0 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Address/AddressDataGenerator.php @@ -0,0 +1,24 @@ + mt_rand(10000, 99999) + ]; + } +} diff --git a/setup/src/Magento/Setup/Model/AdminAccount.php b/setup/src/Magento/Setup/Model/AdminAccount.php index 6605052cede1e..6acb57dcf6cbb 100644 --- a/setup/src/Magento/Setup/Model/AdminAccount.php +++ b/setup/src/Magento/Setup/Model/AdminAccount.php @@ -1,6 +1,6 @@ getHeaders() as $key) { if (isset($row[$key])) { if (is_callable($row[$key])) { - $row[$key] = call_user_func($row[$key], $index); + $row[$key] = call_user_func($row[$key], $index, $generatorKey); } else { $row[$key] = str_replace('%s', $index, $row[$key]); } diff --git a/setup/src/Magento/Setup/Model/ConfigGenerator.php b/setup/src/Magento/Setup/Model/ConfigGenerator.php index 140f5a739889c..b070da8ada199 100644 --- a/setup/src/Magento/Setup/Model/ConfigGenerator.php +++ b/setup/src/Magento/Setup/Model/ConfigGenerator.php @@ -1,6 +1,6 @@ set( - ObjectManagerFactory::CONFIG_PATH_DEFINITION_FORMAT, - $data[ConfigOptionsListConstants::INPUT_KEY_DEFINITION_FORMAT] - ); - } - - return $configData; + return null; } /** diff --git a/setup/src/Magento/Setup/Model/ConfigModel.php b/setup/src/Magento/Setup/Model/ConfigModel.php index cc956ad443ebd..f1fd56abede81 100644 --- a/setup/src/Magento/Setup/Model/ConfigModel.php +++ b/setup/src/Magento/Setup/Model/ConfigModel.php @@ -1,6 +1,6 @@ groupCollectionFactory = $groupCollectionFactory; + $this->addressDataGenerator = $addressDataGenerator; + $this->config = $config; + } + + /** + * Generate customer data by index + * + * @param int $customerId + * @return array + */ + public function generate($customerId) + { + return [ + 'customer' => [ + 'email' => sprintf('user_%s@example.com', $customerId), + 'group_id' => $this->getGroupIdForCustomer($customerId) + ], + + 'addresses' => $this->generateAddresses(), + ]; + } + + /** + * Get customer group id for customer + * @param int $customerId + * @return int + */ + private function getGroupIdForCustomer($customerId) + { + if (!$this->customerGroupIds) { + $this->customerGroupIds = $this->groupCollectionFactory->create()->getAllIds(); + } + + return $this->customerGroupIds[$customerId % count($this->customerGroupIds)]; + } + + /** + * Generate customer addresses with distribution + * 50% as shipping address + * 50% as billing address + * + * @return array + */ + private function generateAddresses() + { + $addresses = []; + $addressesCount = $this->config['addresses-count']; + + while ($addressesCount) { + $addresses[] = $this->addressDataGenerator->generateAddress(); + $addressesCount--; + } + + return $addresses; + } +} diff --git a/setup/src/Magento/Setup/Model/Customer/CustomerDataGeneratorFactory.php b/setup/src/Magento/Setup/Model/Customer/CustomerDataGeneratorFactory.php new file mode 100644 index 0000000000000..5ef7560cacc50 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Customer/CustomerDataGeneratorFactory.php @@ -0,0 +1,44 @@ +objectManager = $objectManager; + } + + /** + * Create CustomerGenerator instance with specified configuration + * + * @param array $config + * @return \Magento\Setup\Model\Customer\CustomerDataGenerator + */ + public function create(array $config) + { + return $this->objectManager->create( + \Magento\Setup\Model\Customer\CustomerDataGenerator::class, + [ + 'addressGenerator' => $this->objectManager->create( + \Magento\Setup\Model\Address\AddressDataGenerator::class + ), + 'config' => $config + ] + ); + } +} diff --git a/setup/src/Magento/Setup/Model/DataGenerator.php b/setup/src/Magento/Setup/Model/DataGenerator.php new file mode 100644 index 0000000000000..4952a05e37e7f --- /dev/null +++ b/setup/src/Magento/Setup/Model/DataGenerator.php @@ -0,0 +1,87 @@ +dictionaryFile = $dictionaryFile; + $this->readData(); + $this->generatedValues = []; + } + + /** + * Read data from file. + * + * @return void + */ + protected function readData() + { + $f = fopen($this->dictionaryFile, 'r'); + while (!feof($f) && is_array($line = fgetcsv($f))) { + $this->dictionaryData[] = $line[0]; + } + } + + /** + * Generate string of random word data. + * + * @param int $minAmountOfWords + * @param int $maxAmountOfWords + * @param string|null $key + * @return string + */ + public function generate($minAmountOfWords, $maxAmountOfWords, $key = null) + { + $numberOfWords = mt_rand($minAmountOfWords, $maxAmountOfWords); + $result = ''; + + if ($key === null || !array_key_exists($key, $this->generatedValues)) { + for ($i = 0; $i < $numberOfWords; $i++) { + $result .= ' ' . $this->dictionaryData[mt_rand(0, count($this->dictionaryData) - 1)]; + } + $result = trim($result); + + if ($key !== null) { + $this->generatedValues[$key] = $result; + } + } else { + $result = $this->generatedValues[$key]; + } + return $result; + } +} diff --git a/setup/src/Magento/Setup/Model/DateTime/DateTimeProvider.php b/setup/src/Magento/Setup/Model/DateTime/DateTimeProvider.php index 1ac281a8d37c4..daf39705592f4 100644 --- a/setup/src/Magento/Setup/Model/DateTime/DateTimeProvider.php +++ b/setup/src/Magento/Setup/Model/DateTime/DateTimeProvider.php @@ -1,6 +1,6 @@ defaultDescription = $defaultDescription; + } + + /** + * @param int $entityIndex + * @return string + */ + public function generate($entityIndex) + { + return sprintf($this->defaultDescription, $entityIndex); + } +} diff --git a/setup/src/Magento/Setup/Model/DependencyReadinessCheck.php b/setup/src/Magento/Setup/Model/DependencyReadinessCheck.php index 3845def7d2130..b485c574af2c5 100644 --- a/setup/src/Magento/Setup/Model/DependencyReadinessCheck.php +++ b/setup/src/Magento/Setup/Model/DependencyReadinessCheck.php @@ -1,6 +1,6 @@ paragraphGenerator = $paragraphGenerator; + $this->mixinManager = $mixinManager; + $this->descriptionConfig = $descriptionConfig; + } + + /** + * Generate description and apply mixin to it + * + * @return string + */ + public function generate() + { + $description = $this->generateRawDescription(); + + if (isset($this->descriptionConfig['mixin'])) { + $description = $this->mixinManager->apply($description, $this->descriptionConfig['mixin']['tags']); + } + + return $description; + } + + /** + * Generate raw description without mixin + * + * @return string + */ + private function generateRawDescription() + { + $paragraphsCount = mt_rand( + $this->descriptionConfig['paragraphs']['count-min'], + $this->descriptionConfig['paragraphs']['count-max'] + ); + $descriptionParagraphs = ''; + + while ($paragraphsCount) { + $descriptionParagraphs .= $this->paragraphGenerator->generate(); + $descriptionParagraphs .= PHP_EOL; + $paragraphsCount--; + } + + $descriptionParagraphs = rtrim($descriptionParagraphs); + + return $descriptionParagraphs; + } +} diff --git a/setup/src/Magento/Setup/Model/Description/DescriptionParagraphGenerator.php b/setup/src/Magento/Setup/Model/Description/DescriptionParagraphGenerator.php new file mode 100644 index 0000000000000..aaeb390d540e6 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/DescriptionParagraphGenerator.php @@ -0,0 +1,58 @@ +sentenceGenerator = $sentenceGenerator; + $this->paragraphConfig = $paragraphConfig; + } + + /** + * Generate paragraph for description + * + * @return string + */ + public function generate() + { + $sentencesCount = mt_rand( + $this->paragraphConfig['sentences']['count-min'], + $this->paragraphConfig['sentences']['count-max'] + ); + $sentences = ''; + + while ($sentencesCount) { + $sentences .= $this->sentenceGenerator->generate(); + $sentences .= ' '; + $sentencesCount--; + } + + $sentences = rtrim($sentences); + + return $sentences; + } +} diff --git a/setup/src/Magento/Setup/Model/Description/DescriptionSentenceGenerator.php b/setup/src/Magento/Setup/Model/Description/DescriptionSentenceGenerator.php new file mode 100644 index 0000000000000..76f320b74c6f0 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/DescriptionSentenceGenerator.php @@ -0,0 +1,56 @@ +dictionary = $dictionary; + $this->sentenceConfig = $sentenceConfig; + } + + /** + * Generate sentence for description + * + * @return string + */ + public function generate() + { + $sentenceWordsCount = mt_rand( + $this->sentenceConfig['words']['count-min'], + $this->sentenceConfig['words']['count-max'] + ); + $sentence = ''; + + while ($sentenceWordsCount) { + $sentence .= $this->dictionary->getRandWord(); + $sentence .= ' '; + $sentenceWordsCount--; + } + + return ucfirst(rtrim($sentence)) . '.'; + } +} diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/BoldMixin.php b/setup/src/Magento/Setup/Model/Description/Mixin/BoldMixin.php new file mode 100644 index 0000000000000..6b1f3039bc1c4 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/Mixin/BoldMixin.php @@ -0,0 +1,55 @@ +randomWordSelector = $randomWordSelector; + $this->wordWrapper = $wordWrapper; + } + + /** + * Add tag to text at random positions + * + * @param string $text + * @return string + */ + public function apply($text) + { + if (empty(strip_tags(trim($text)))) { + return $text; + } + + $rawText = strip_tags($text); + + return $this->wordWrapper->wrapWords( + $text, + $this->randomWordSelector->getRandomWords($rawText, mt_rand(5, 8)), + '%s' + ); + } +} diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/BrakeMixin.php b/setup/src/Magento/Setup/Model/Description/Mixin/BrakeMixin.php new file mode 100644 index 0000000000000..12a492f6ec752 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/Mixin/BrakeMixin.php @@ -0,0 +1,26 @@ + tag to text after each new line (\r\n) + * + * @param string $text + * @return string + */ + public function apply($text) + { + return implode( + PHP_EOL . '
    ' . PHP_EOL, + explode(PHP_EOL, trim($text)) + ); + } +} diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/DescriptionMixinInterface.php b/setup/src/Magento/Setup/Model/Description/Mixin/DescriptionMixinInterface.php new file mode 100644 index 0000000000000..88422e8038cf8 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/Mixin/DescriptionMixinInterface.php @@ -0,0 +1,20 @@ + header with text before each new line (\r\n) + * + * @param string $text + * @return string + */ + public function apply($text) + { + $paragraphs = explode(PHP_EOL, trim($text)); + $magicLengthNumber = 10; + + foreach ($paragraphs as &$paragraph) { + $rawText = trim(strip_tags($paragraph)); + if (empty($rawText)) { + continue; + } + + $headerText = substr($rawText, 0, strpos($rawText, ' ', $magicLengthNumber)); + $paragraph = '

    ' . $headerText . '

    ' . PHP_EOL . $paragraph; + } + + return implode(PHP_EOL, $paragraphs); + } +} diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/Helper/RandomWordSelector.php b/setup/src/Magento/Setup/Model/Description/Mixin/Helper/RandomWordSelector.php new file mode 100644 index 0000000000000..77ac097f6f524 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/Mixin/Helper/RandomWordSelector.php @@ -0,0 +1,36 @@ +randomWordSelector = $randomWordSelector; + $this->wordWrapper = $wordWrapper; + } + + /** + * Add tag to text at random positions + * + * @param string $text + * @return string + */ + public function apply($text) + { + if (empty(strip_tags(trim($text)))) { + return $text; + } + + $rawText = strip_tags($text); + + return $this->wordWrapper->wrapWords( + $text, + $this->randomWordSelector->getRandomWords($rawText, mt_rand(5, 8)), + '%s' + ); + } +} diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/MixinFactory.php b/setup/src/Magento/Setup/Model/Description/Mixin/MixinFactory.php new file mode 100644 index 0000000000000..b3de1c390dd33 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/Mixin/MixinFactory.php @@ -0,0 +1,76 @@ + SpanMixin::class, + self::BOLD_MIXIN => BoldMixin::class, + self::BRAKE_MIXIN => BrakeMixin::class, + self::PARAGRAPH_MIXIN => ParagraphMixin::class, + self::HEADER_MIXIN => HeaderMixin::class, + self::ITALIC_MIXIN => ItalicMixin::class, + ]; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @param \Magento\Framework\ObjectManagerInterface $objectManager + * @throws \Magento\Setup\Exception + */ + public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * Create mixin by type + * + * @param string $mixinType + * @return \Magento\Setup\Model\Description\Mixin\DescriptionMixinInterface + * @throws \InvalidArgumentException + */ + public function create($mixinType) + { + if (!isset($this->typeClassMap[$mixinType])) { + throw new \InvalidArgumentException(sprintf('Undefined mixin type: %s.', $mixinType)); + } + + $mixin = $this->objectManager->get($this->typeClassMap[$mixinType]); + + if (!$mixin instanceof \Magento\Setup\Model\Description\Mixin\DescriptionMixinInterface) { + throw new \InvalidArgumentException( + sprintf( + 'Class "%s" must implement \Magento\Setup\Model\Description\Mixin\DescriptionMixinInterface.', + get_class($mixin) + ) + ); + } + + return $mixin; + } +} diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/ParagraphMixin.php b/setup/src/Magento/Setup/Model/Description/Mixin/ParagraphMixin.php new file mode 100644 index 0000000000000..09ab154dbdbd0 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/Mixin/ParagraphMixin.php @@ -0,0 +1,28 @@ +

    tags + * + * @param string $text + * @return string + */ + public function apply($text) + { + return '

    ' + . implode( + '

    ' . PHP_EOL . '

    ', + explode(PHP_EOL, trim($text)) + ) + . '

    '; + } +} diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/SpanMixin.php b/setup/src/Magento/Setup/Model/Description/Mixin/SpanMixin.php new file mode 100644 index 0000000000000..b1fb1ecb25475 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/Mixin/SpanMixin.php @@ -0,0 +1,55 @@ +randomWordSelector = $randomWordSelector; + $this->wordWrapper = $wordWrapper; + } + + /** + * Add tag to text at random positions + * + * @param string $text + * @return string + */ + public function apply($text) + { + if (empty(strip_tags(trim($text)))) { + return $text; + } + + $rawText = strip_tags($text); + + return $this->wordWrapper->wrapWords( + $text, + $this->randomWordSelector->getRandomWords($rawText, mt_rand(5, 8)), + '%s' + ); + } +} diff --git a/setup/src/Magento/Setup/Model/Description/MixinManager.php b/setup/src/Magento/Setup/Model/Description/MixinManager.php new file mode 100644 index 0000000000000..1dd627c322ec4 --- /dev/null +++ b/setup/src/Magento/Setup/Model/Description/MixinManager.php @@ -0,0 +1,41 @@ +mixinFactory = $mixinFactory; + } + + /** + * Apply list of mixin to description + * + * @param string $description + * @param array $mixinList + * @return mixed + */ + public function apply($description, array $mixinList) + { + foreach ($mixinList as $mixinType) { + $description = $this->mixinFactory->create($mixinType)->apply($description); + } + + return $description; + } +} diff --git a/setup/src/Magento/Setup/Model/DescriptionGeneratorInterface.php b/setup/src/Magento/Setup/Model/DescriptionGeneratorInterface.php new file mode 100644 index 0000000000000..2db798f8c1a33 --- /dev/null +++ b/setup/src/Magento/Setup/Model/DescriptionGeneratorInterface.php @@ -0,0 +1,20 @@ +dictionaryFilePath = $dictionaryFilePath; + } + + /** + * Returns random word from dictionary + * + * @return string + */ + public function getRandWord() + { + if ($this->dictionary === null) { + $this->readDictionary(); + } + + $randIndex = mt_rand(0, count($this->dictionary) - 1); + return trim($this->dictionary[$randIndex]); + } + + /** + * Read dictionary file + * + * @return void + * @throws \Magento\Setup\Exception + */ + private function readDictionary() + { + if (!is_readable($this->dictionaryFilePath)) { + throw new \Magento\Setup\Exception( + sprintf('Description file %s not found or is not readable', $this->dictionaryFilePath) + ); + } + + $rows = file($this->dictionaryFilePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + + if ($rows === false) { + throw new \Magento\Setup\Exception( + sprintf('Error occurred while reading dictionary file %s', $this->dictionaryFilePath) + ); + } + + if (empty($rows)) { + throw new \Magento\Setup\Exception( + sprintf('Dictionary file %s is empty', $this->dictionaryFilePath) + ); + } + + $this->dictionary = \SplFixedArray::fromArray($rows); + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/BundleProductGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/BundleProductGenerator.php new file mode 100644 index 0000000000000..44efd9ff208fe --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/BundleProductGenerator.php @@ -0,0 +1,119 @@ + simple product sku pattern, which will be used as configurable variation, + * '_bundle_options' => amount of options per bundle product, + * '_bundle_products_per_option' => amount of simple products per each option, + * ] + * @see ProductGenerator + * @see BundleProductTemplateGenerator + */ +class BundleProductGenerator +{ + /** + * @var ProductGeneratorFactory + */ + private $productGeneratorFactory; + + /** + * @param ProductGeneratorFactory $productGeneratorFactory + */ + public function __construct(ProductGeneratorFactory $productGeneratorFactory) + { + $this->productGeneratorFactory = $productGeneratorFactory; + } + + /** + * Generate bundle products products + * + * @param int $products + * @param array $fixtureMap + * @return void + */ + public function generate($products, $fixtureMap) + { + $this->productGeneratorFactory->create([ + 'customTableMap' => [ + 'catalog_product_bundle_option_value' => [ + 'entity_id_field' => EntityGenerator::SKIP_ENTITY_ID_BINDING, + 'handler' => function ($productId, $entityNumber, $fixture, $binds) { + foreach ($binds as &$bind) { + $bind['option_id'] = $this->generateOptionId($bind['option_id'], $entityNumber, $fixture); + } + return $binds; + }, + ], + 'catalog_product_bundle_selection' => [ + 'entity_id_field' => EntityGenerator::SKIP_ENTITY_ID_BINDING, + 'handler' => function ($productId, $entityNumber, $fixture, $binds) { + foreach ($binds as &$bind) { + $bind['option_id'] = $this->generateOptionId($bind['option_id'], $entityNumber, $fixture); + $bind['parent_product_id'] = $productId; + $simpleProductId = $this->generateSimpleProductId( + $bind['product_id'], + $entityNumber, + $fixture + ); + $bind['product_id'] = $simpleProductId; + $bind['selection_price_value'] = $fixture['price']($simpleProductId); + $bind['selection_price_type'] = $fixture['priceType']($simpleProductId); + } + return $binds; + }, + ], + 'catalog_product_relation' => [ + 'entity_id_field' => EntityGenerator::SKIP_ENTITY_ID_BINDING, + 'handler' => function ($productId, $entityNumber, $fixture, $binds) { + foreach ($binds as &$bind) { + $bind['parent_id'] = $productId; + $bind['child_id'] = $this->generateSimpleProductId( + $bind['child_id'], + $entityNumber, + $fixture + ); + } + return $binds; + }, + ], + ] + ])->generate($products, $fixtureMap); + } + + /** + * Generate value of option_id for $entityNumber bundle product based on previous option_id + * + * @param int $previousOptionId + * @param int $entityNumber + * @param array $fixture + * @return int + */ + private function generateOptionId($previousOptionId, $entityNumber, array $fixture) + { + return $previousOptionId + $entityNumber * $fixture['_bundle_options'] + $fixture['_bundle_options']; + } + + /** + * Generate value of simple product id which is used for $entityNumber bundle product as option item + * + * @param int $previousProductId + * @param int $entityNumber + * @param array $fixture + * @return mixed + */ + private function generateSimpleProductId($previousProductId, $entityNumber, array $fixture) + { + return $previousProductId + + $entityNumber * $fixture['_bundle_products_per_option'] * $fixture['_bundle_options']; + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/BundleProductTemplateGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/BundleProductTemplateGenerator.php new file mode 100644 index 0000000000000..182cc19a97940 --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/BundleProductTemplateGenerator.php @@ -0,0 +1,164 @@ +fixture = $fixture; + $this->productFactory = $productFactory; + $this->optionFactory = $optionFactory; + $this->linkFactory = $linkFactory; + } + + /** + * {@inheritdoc} + */ + public function generateEntity() + { + $product = $this->getProductTemplate( + $this->fixture['attribute_set_id'] + ); + $product->save(); + + return $product; + } + + /** + * Get product template + * + * @param int $attributeSet + * @return ProductInterface + */ + private function getProductTemplate($attributeSet) + { + $bundleOptions = $this->fixture['_bundle_options']; + $bundleProductsPerOption = $this->fixture['_bundle_products_per_option']; + $bundleVariationSkuPattern = $this->fixture['_bundle_variation_sku_pattern']; + $productRandomizerNumber = crc32(mt_rand(1, PHP_INT_MAX)); + $bundleProduct = $this->productFactory->create([ + 'data' => [ + 'attribute_set_id' => $attributeSet, + 'type_id' => Type::TYPE_BUNDLE, + 'name' => 'template name' . $productRandomizerNumber, + 'url_key' => 'template-url' . $productRandomizerNumber, + 'sku' => 'template_sku' . $productRandomizerNumber, + 'price' => 10, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'status' => Status::STATUS_ENABLED, + 'website_ids' => [1], + 'category_ids' => isset($this->fixture['category_ids']) ? [2] : null, + 'weight' => 1, + 'description' => 'description', + 'short_description' => 'short description', + 'tax_class_id' => 2, //'taxable goods', + 'price_type' => \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED, + 'price_view' => 1, + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'qty' => 100500, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1 + ], + 'can_save_bundle_selections' => true, + 'affect_bundle_product_selections' => true, + + ] + ]); + + $bundleProductOptions = []; + $variationN = 0; + for ($i = 1; $i <= $bundleOptions; $i++) { + $option = $this->optionFactory->create(['data' => [ + 'title' => 'Bundle Product Items ' . $i, + 'default_title' => 'Bundle Product Items ' . $i, + 'type' => 'select', + 'required' => 1, + 'delete' => '', + 'position' => $bundleOptions - $i, + 'option_id' => '', + ]]); + $option->setSku($bundleProduct->getSku()); + $option->setOptionId(null); + + $links = []; + for ($linkN = 1; $linkN <= $bundleProductsPerOption; $linkN++) { + $variationN++; + $link = $this->linkFactory->create(['data' => [ + 'sku' => sprintf($bundleVariationSkuPattern, $variationN), + 'qty' => 1, + 'can_change_qty' => 1, + 'position' => $linkN - 1, + 'price_type' => 0, + 'price' => 0.0, + 'option_id' => '', + 'is_default' => $linkN === 1, + ]]); + $links[] = $link; + } + $option->setProductLinks($links); + $bundleProductOptions[] = $option; + } + + $extension = $bundleProduct->getExtensionAttributes(); + $extension->setBundleProductOptions($bundleProductOptions); + $bundleProduct->setExtensionAttributes($extension); + // Need for set "has_options" field + $bundleProduct->setBundleOptionsData($bundleProductOptions); + $bundleSelections = array_map(function ($option) { + return array_map(function ($link) { + return $link->getData(); + }, $option->getProductLinks()); + }, $bundleProductOptions); + $bundleProduct->setBundleSelectionsData($bundleSelections); + + return $bundleProduct; + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/ConfigurableProductGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/ConfigurableProductGenerator.php new file mode 100644 index 0000000000000..800703806481e --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/ConfigurableProductGenerator.php @@ -0,0 +1,119 @@ + simple product sku pattern, which will be used as configurable variation, + * '_attributes_count' => amount of attributes on which configurable product is based, + * '_variation_count' => amount of generated variations, + * '_attributes' => product attributes on which configurable product is based , + * ] + * @see ProductGenerator + * @see ConfigurableProductTemplateGenerator + */ +class ConfigurableProductGenerator +{ + /** + * @var ProductGeneratorFactory + */ + private $productGeneratorFactory; + + /** + * @param ProductGeneratorFactory $productGeneratorFactory + */ + public function __construct(ProductGeneratorFactory $productGeneratorFactory) + { + $this->productGeneratorFactory = $productGeneratorFactory; + } + + /** + * Generate bundle products products + * + * @param int $products + * @param array $fixtureMap + * @return void + */ + public function generate($products, $fixtureMap) + { + $this->productGeneratorFactory->create([ + 'customTableMap' => [ + 'catalog_product_super_attribute_label' => [ + 'entity_id_field' => EntityGenerator::SKIP_ENTITY_ID_BINDING, + 'handler' => function ($productId, $entityNumber, $fixture, $binds) { + foreach ($binds as &$bind) { + $bind['product_super_attribute_id'] = $this->generateSuperAttributeId( + $bind['product_super_attribute_id'], + $entityNumber, + $fixture + ); + } + return $binds; + }, + ], + 'catalog_product_super_link' => [ + 'entity_id_field' => EntityGenerator::SKIP_ENTITY_ID_BINDING, + 'handler' => function ($productId, $entityNumber, $fixture, $binds) { + foreach ($binds as &$bind) { + $bind['parent_id'] = $productId; + $bind['product_id'] = $this->generateSimpleProductId( + $bind['product_id'], + $entityNumber, + $fixture + ); + } + return $binds; + }, + ], + 'catalog_product_relation' => [ + 'entity_id_field' => EntityGenerator::SKIP_ENTITY_ID_BINDING, + 'handler' => function ($productId, $entityNumber, $fixture, $binds) { + foreach ($binds as &$bind) { + $bind['parent_id'] = $productId; + $bind['child_id'] = $this->generateSimpleProductId( + $bind['child_id'], + $entityNumber, + $fixture + ); + } + return $binds; + }, + ], + ] + ])->generate($products, $fixtureMap); + } + + /** + * Generate value of option_id for $entityNumber bundle product based on previous option_id + * + * @param int $superAttributeId + * @param int $entityNumber + * @param array $fixture + * @return int + */ + private function generateSuperAttributeId($superAttributeId, $entityNumber, array $fixture) + { + return $superAttributeId + $entityNumber * $fixture['_attributes_count'] + $fixture['_attributes_count']; + } + + /** + * Generate value of simple product id which is used for $entityNumber bundle product as option item + * + * @param int $previousProductId + * @param int $entityNumber + * @param array $fixture + * @return mixed + */ + private function generateSimpleProductId($previousProductId, $entityNumber, array $fixture) + { + return $previousProductId + $entityNumber * $fixture['_variation_count']; + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/ConfigurableProductTemplateGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/ConfigurableProductTemplateGenerator.php new file mode 100644 index 0000000000000..71916c611e1fa --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/ConfigurableProductTemplateGenerator.php @@ -0,0 +1,164 @@ +fixture = $fixture; + $this->productFactory = $productFactory; + $this->optionFactory = $optionFactory; + $this->resourceConnection = $resourceConnection; + } + + /** + * {@inheritdoc} + */ + public function generateEntity() + { + $attributeSet = $this->fixture['attribute_set_id']; + $product = $this->getProductTemplate($attributeSet); + + $product->save(); + + return $product; + } + + /** + * Get product template + * + * @param int $attributeSet + * @return ProductInterface + */ + private function getProductTemplate($attributeSet) + { + $productRandomizerNumber = crc32(mt_rand(1, PHP_INT_MAX)); + $product = $this->productFactory->create([ + 'data' => [ + 'attribute_set_id' => $attributeSet, + 'type_id' => Configurable::TYPE_CODE, + 'name' => 'template name' . $productRandomizerNumber, + 'url_key' => 'template-url' . $productRandomizerNumber, + 'sku' => 'template_sku' . $productRandomizerNumber, + 'meta_description' => 'Configurable Product', + 'meta_keyword' => $productRandomizerNumber, + 'meta_title' => $productRandomizerNumber, + 'price' => 10, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'status' => Status::STATUS_ENABLED, + 'website_ids' => (array)$this->fixture['website_ids'](1, 0), + 'category_ids' => isset($this->fixture['category_ids']) ? [2] : null, + 'weight' => 1, + 'description' => 'description', + 'short_description' => 'short description', + 'tax_class_id' => 2, //'taxable goods', + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'qty' => 100500, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1 + ], + // Need for set "has_options" field + 'can_save_configurable_attributes' => true, + 'configurable_attributes_data' => $this->fixture['_attributes'], + ] + ]); + + $attributes = []; + foreach ($this->fixture['_attributes'] as $index => $attribute) { + $attributeValues = []; + foreach ($attribute['values'] as $value) { + $attributeValues[] = [ + 'label' => $attribute['name'], + 'attribute_id' => $attribute['id'], + 'value_index' => $value + ]; + } + $attributes[] = [ + 'attribute_id' => $attribute['id'], + 'code' => $attribute['name'], + 'label' => $attribute['name'], + 'position' => $index, + 'values' => $attributeValues, + ]; + } + $configurableOptions = $this->optionFactory->create($attributes); + $extensionConfigurableAttributes = $product->getExtensionAttributes(); + $extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); + $extensionConfigurableAttributes->setConfigurableProductLinks($this->getAssociatedProductIds()); + $product->setExtensionAttributes($extensionConfigurableAttributes); + + return $product; + } + + /** + * Get configurable variation ids. Retrieve first simple product id by sku pattern from DB and generate next values + * for all variations + * + * @return array + */ + private function getAssociatedProductIds() + { + $associatedProductIds = []; + $connection = $this->resourceConnection->getConnection(); + $firstSimpleProductId = $connection->fetchRow( + $connection->select() + ->from($this->resourceConnection->getTableName('catalog_product_entity')) + ->where('sku = ?', $this->fixture['_variation_sku_pattern']) + )['entity_id']; + + for ($i = 0; $i < $this->fixture['_variation_count']; $i++) { + $associatedProductIds[] = $firstSimpleProductId + $i; + } + + return $associatedProductIds; + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/CustomerGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/CustomerGenerator.php new file mode 100644 index 0000000000000..7b66e26748667 --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/CustomerGenerator.php @@ -0,0 +1,158 @@ +entityGeneratorFactory = $entityGeneratorFactory; + $this->customerTemplateGenerator = $customerTemplateGenerator; + $this->resourceConnection = $resourceConnection; + } + + /** + * Generate entities + * + * @param int $customers + * @param array $fixtureMap + * @return void + */ + public function generate($customers, array $fixtureMap) + { + $this->entityGeneratorFactory + ->create([ + 'entityType' => CustomerInterface::class, + 'customTableMap' => [ + 'customer_entity' => [ + 'handler' => $this->getCustomerEntityHandler() + ], + + 'customer_address_entity' => [ + 'handler' => $this->getCustomerAddressEntityHandler() + ] + ], + ])->generate( + $this->customerTemplateGenerator, + $customers, + function ($customerId) use ($fixtureMap) { + $fixtureMap['customer_data'] = call_user_func($fixtureMap['customer_data'], $customerId); + return $fixtureMap; + } + ); + + $this->addDefaultAddresses(); + } + + /** + * Creates closure that is used + * to replace default customer data with data from fixture + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @return \Closure + */ + private function getCustomerEntityHandler() + { + return function ($entityId, $entityNumber, $fixtureMap, $binds) { + return array_map( + 'array_merge', + $binds, + array_fill(0, count($binds), $fixtureMap['customer_data']['customer']) + ); + }; + } + + /** + * Creates closure that is used + * to replace default customer address data with data from fixture + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @return \Closure + */ + private function getCustomerAddressEntityHandler() + { + return function ($entityId, $entityNumber, $fixtureMap, $binds) { + return array_map( + 'array_merge', + array_fill(0, count($fixtureMap['customer_data']['addresses']), reset($binds)), + $fixtureMap['customer_data']['addresses'] + ); + }; + } + + /** + * Set default billing and shipping addresses for customer + * + * @return void + */ + private function addDefaultAddresses() + { + $this->getConnection()->query( + sprintf( + ' + update `%s` customer + join ( + select + parent_id, min(entity_id) as min, max(entity_id) as max + from `%s` + group by parent_id + ) customer_address on customer_address.parent_id = customer.entity_id + set + customer.default_billing = customer_address.min, + customer.default_shipping = customer_address.max + ', + $this->resourceConnection->getTableName('customer_entity'), + $this->resourceConnection->getTableName('customer_address_entity') + ) + ); + } + + /** + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ + private function getConnection() + { + if (null === $this->connection) { + $this->connection = $this->resourceConnection->getConnection(); + } + + return $this->connection; + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/CustomerTemplateGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/CustomerTemplateGenerator.php new file mode 100644 index 0000000000000..d324deb28ea28 --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/CustomerTemplateGenerator.php @@ -0,0 +1,132 @@ +customerFactory = $customerFactory; + $this->addressFactory = $addressFactory; + $this->storeManager = $storeManager; + } + + /** + * {@inheritdoc} + */ + public function generateEntity() + { + $customer = $this->getCustomerTemplate(); + $customer->save(); + $address = $this->getAddressTemplate($customer->getId()); + $address->save(); + + return $customer; + } + + /** + * Get customer template + * + * @return Customer + */ + private function getCustomerTemplate() + { + $customerRandomizerNumber = crc32(mt_rand(1, PHP_INT_MAX)); + + $now = new \DateTime(); + + return $this->customerFactory->create([ + 'data' => [ + 'email' => sprintf('user_%s@example.com', $customerRandomizerNumber), + 'confirmation' => null, + 'created_at' => $now->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT), + 'created_in' => 'Default', + 'default_billing' => '1', + 'default_shipping' => '1', + 'disable_auto_group_change' => '0', + 'dob' => '12-10-1991', + 'firstname' => 'Firstname', + 'gender' => 1, + 'group_id' => '1', + 'lastname' => 'Lastname', + 'middlename' => '', + 'password_hash' => '', + 'prefix' => null, + 'rp_token' => null, + 'rp_token_created_at' => null, + 'store_id' => $this->storeManager->getDefaultStoreView()->getId(), + 'suffix' => null, + 'taxvat' => null, + 'website_id' => $this->storeManager->getDefaultStoreView()->getWebsiteId(), + 'password' => '123123q', + ] + ]); + } + + /** + * @param int $customerId + * @return Address + */ + private function getAddressTemplate($customerId) + { + return $this->addressFactory->create([ + 'data' => [ + 'parent_id' => $customerId, + 'attribute_set_id' => 2, + 'telephone' => 3468676, + 'postcode' => 75477, + 'country_id' => 'US', + 'city' => 'CityM', + 'company' => 'CompanyName', + 'street' => 'Green str, 67', + 'lastname' => 'Smith', + 'firstname' => 'John', + 'region_id' => 1, + 'fax' => '04040404', + 'middlename' => '', + 'prefix' => '', + 'region' => 'Arkansas', + 'suffix' => '', + 'vat_id' => '', + 'default_billing_' => '1', + 'default_shipping_' => '1', + ] + ]); + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/EntityGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/EntityGenerator.php new file mode 100644 index 0000000000000..ebc121b8c06f2 --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/EntityGenerator.php @@ -0,0 +1,471 @@ + entity if field name which linked to entity table primary key + * or SKIP_ENTITY_ID_BINDING for do not set entity_id during generation + * 'handler' => function($entityId, $fixture, $binds) callback for process binding for custom table + * 'fields' => [key name in fixture for process custom bindings, ...] + * ] + */ + private $customTableMap; + + /** + * entity table class name + * + * @var string + */ + private $entityType; + + /** + * @var \Magento\Setup\Model\FixtureGenerator\SqlCollector + */ + private $sqlCollector; + + /** + * @var \Magento\Framework\App\ResourceConnection + */ + private $resourceConnection; + + /** + * @var \Magento\Eav\Model\ResourceModel\AttributeLoader + */ + private $attributeLoader; + + /** + * @var \Magento\Eav\Api\Data\AttributeInterface[] + */ + private $attributes; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface + */ + private $connection; + + /** + * @var array + */ + private $tableToEntityIdMap; + + /** + * @var string + */ + private $entityTable; + + /** + * List of tables where entity id information is stored + * + * @var array + */ + private $primaryEntityIdTables; + + /** + * @var \Magento\Framework\EntityManager\MetadataPool + */ + private $metadataPool; + + /** + * @var \Magento\Framework\EntityManager\EntityMetadataInterface + */ + private $entityMetadata; + + /** + * @var \Magento\Framework\EntityManager\Sequence\SequenceRegistry + */ + private $sequenceRegistry; + + /** + * @var bool + */ + private $isMappingInitialized = false; + + /** + * @var int + */ + private $bunchSize; + + /** + * @param SqlCollector $sqlCollector + * @param \Magento\Eav\Model\ResourceModel\AttributeLoader $attributeLoader + * @param \Magento\Framework\App\ResourceConnection $resourceConnection + * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool + * @param \Magento\Framework\EntityManager\Sequence\SequenceRegistry $sequenceRegistry + * @param string $entityType + * @param array $customTableMap + * @param int $bunchSize + */ + public function __construct( + \Magento\Setup\Model\FixtureGenerator\SqlCollector $sqlCollector, + \Magento\Eav\Model\ResourceModel\AttributeLoader $attributeLoader, + \Magento\Framework\App\ResourceConnection $resourceConnection, + \Magento\Framework\EntityManager\MetadataPool $metadataPool, + \Magento\Framework\EntityManager\Sequence\SequenceRegistry $sequenceRegistry, + $entityType, + $customTableMap = [], + $bunchSize = self::SQL_DEFAULT_BUNCH_AMOUNT + ) { + $this->sqlCollector = $sqlCollector; + $this->resourceConnection = $resourceConnection; + $this->attributeLoader = $attributeLoader; + $this->metadataPool = $metadataPool; + $this->sequenceRegistry = $sequenceRegistry; + $this->customTableMap = $customTableMap; + $this->entityType = $entityType; + $this->bunchSize = (int)$bunchSize; + } + + /** + * Generate entities + * + * @param TemplateEntityGeneratorInterface $entityGenerator + * @param int $entitiesAmount + * @param callable $fixture + * @return void + */ + public function generate(TemplateEntityGeneratorInterface $entityGenerator, $entitiesAmount, callable $fixture) + { + $this->sqlCollector->enable(); + $entity = $entityGenerator->generateEntity(); + $this->sqlCollector->disable(); + $entity->delete(); + + $map = []; + $processed = 0; + $entitiesAmount = (int)$entitiesAmount; + gc_disable(); + for ($entityNumber = 0; $entityNumber < $entitiesAmount; $entityNumber++) { + $processed++; + $map = array_merge_recursive($map, $this->getSqlQueries($entity, $entityNumber, $fixture)); + + if ($processed % $this->bunchSize === 0 || $entityNumber === ($entitiesAmount - 1)) { + $this->saveEntities($map); + } + } + gc_enable(); + } + + /** + * Provide list of sql queries for create a new entity + * + * @param object $entity + * @param int $entityNumber + * @param callable $fixtureMap + * @return array + */ + private function getSqlQueries($entity, $entityNumber, callable $fixtureMap) + { + $metadata = $this->getEntityMetadata(); + $this->initializeMapping(); + + $entityId = $entity->getData($metadata->getIdentifierField()) + $entityNumber; + $entityLinkId = $entity->getData($metadata->getLinkField()) + $entityNumber; + $fixtureMap = $fixtureMap($entityId, $entityNumber); + + $sql = []; + foreach ($this->sqlCollector->getSql() as $pattern) { + list ($binds, $table) = $pattern; + + if (!isset($sql[$table])) { + $sql[$table] = []; + } + + foreach ($binds as &$bind) { + if ($table === $this->getEntityTable()) { + $bind[$metadata->getLinkField()] = $entityLinkId; + $bind[$metadata->getIdentifierField()] = $entityId; + } + + if ($bind) { + $this->setNewBindValue($entityId, $entityNumber, $table, $bind, $fixtureMap); + } + if (self::SKIP_ENTITY_ID_BINDING === $this->getEntityIdField($table)) { + continue; + } + if ($this->getEntityIdField($table) === $metadata->getLinkField()) { + $bind[$this->getEntityIdField($table)] = $entityLinkId; + } else { + $bind[$this->getEntityIdField($table)] = $entityId; + } + } + + $binds = $this->bindWithCustomHandler($table, $entityId, $entityNumber, $fixtureMap, $binds); + $sql[$table] = array_merge($sql[$table], $binds); + } + + return $sql; + } + + /** + * If custom handler passed for table then override binds with it + * + * @param string $table + * @param int $entityId + * @param int $entityNumber + * @param array $fixtureMap + * @param array $binds + * @return array + */ + private function bindWithCustomHandler($table, $entityId, $entityNumber, $fixtureMap, $binds) + { + if (isset($this->customTableMap[$table]['handler']) + && is_callable($this->customTableMap[$table]['handler']) + ) { + $binds = $this->customTableMap[$table]['handler']($entityId, $entityNumber, $fixtureMap, $binds); + } + + return $binds; + } + + /** + * Save entities to DB and reset entities holder + * + * @param array $map + * @return void + */ + private function saveEntities(array &$map) + { + foreach ($map as $table => $data) { + $this->getConnection()->insertMultiple($table, $data); + } + + $map = []; + } + + /** + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ + private function getConnection() + { + if (null === $this->connection) { + $this->connection = $this->resourceConnection->getConnection(); + } + + return $this->connection; + } + + /** + * @return \Magento\Framework\EntityManager\EntityMetadataInterface + */ + private function getEntityMetadata() + { + if (null === $this->entityMetadata) { + $this->entityMetadata = $this->metadataPool->getMetadata($this->entityType); + } + + return $this->entityMetadata; + } + + /** + * Get entity table name + * + * @return string + */ + private function getEntityTable() + { + if (null === $this->entityTable) { + $this->entityTable = $this->getEntityMetadata()->getEntityTable(); + } + + return $this->entityTable; + } + + /** + * Get field name for specific table where stored link to primary key of entity table + * Find field by FK to entity table + * + * @param string $table + * @return string + * @throws ValidatorException + */ + private function getEntityIdField($table) + { + if (!isset($this->tableToEntityIdMap[$table])) { + $foreignKey = null; + foreach ($this->primaryEntityIdTables as $primaryTable) { + $foreignKey = array_filter( + $this->getConnection()->getForeignKeys($table), + function ($ddl) use ($primaryTable) { + return $ddl['REF_TABLE_NAME'] === $primaryTable + && $ddl['REF_COLUMN_NAME'] === $this->getEntityIdField($primaryTable); + } + ); + if ($foreignKey) { + break; + } + } + if (!$foreignKey) { + throw new ValidatorException(__('Cannot find entity id field for table "%1"', $table)); + } + $this->tableToEntityIdMap[$table] = current($foreignKey)['COLUMN_NAME']; + } + + return $this->tableToEntityIdMap[$table]; + } + + /** + * Initialize map between table and entity id and convert table name to valid table name + * + * @return void + * @throws ValidatorException + */ + private function initializeMapping() + { + if (!$this->isMappingInitialized) { + $this->isMappingInitialized = true; + + $this->initCustomTables(); + + $this->primaryEntityIdTables = [ + $this->getEntityMetadata()->getEntityTable() + ]; + $entitySequence = $this->sequenceRegistry->retrieve($this->entityType); + if (isset($entitySequence['sequenceTable'])) { + $this->primaryEntityIdTables[] = $this->resourceConnection->getTableName( + $entitySequence['sequenceTable'] + ); + } + + foreach ($this->primaryEntityIdTables as $table) { + $ddl = array_filter( + $this->getConnection()->describeTable($table), + function ($data) { + return $data['PRIMARY'] === true; + } + ); + if (!$ddl) { + throw new ValidatorException(__('Cannot find primary key for table "%1"', $table)); + } + $this->tableToEntityIdMap[$table] = current($ddl)['COLUMN_NAME']; + } + } + } + + /** + * Rebind table name with real name, initialize table map for tables without foreign key to entity table + * + * @return void + */ + private function initCustomTables() + { + $customTableData = [ + 'entity_id_field' => null, + 'handler' => null, + 'fields' => [], + ]; + $customTableMap = []; + foreach ($this->customTableMap as $table => $data) { + $table = $this->resourceConnection->getTableName($table); + $data = array_merge($customTableData, $data); + $customTableMap[$table] = $data; + if ($data['entity_id_field']) { + $this->tableToEntityIdMap[$table] = $data['entity_id_field']; + } + } + $this->customTableMap = $customTableMap; + } + + /** + * Get EAV attributes metadata for non-static attributes + * + * @return array + */ + private function getAttributesMetadata() + { + if (null === $this->attributes) { + foreach ($this->attributeLoader->getAttributes($this->entityType) as $attribute) { + if ($attribute->isStatic()) { + continue; + } + $this->attributes[$attribute->getBackendTable()][$attribute->getAttributeCode()] = [ + 'value_field' => 'value', + 'link_field' => 'attribute_id', + 'attribute_id' => $attribute->getAttributeId(), + ]; + } + } + + return $this->attributes; + } + + /** + * Set new bind value for new record + * + * @param int $entityId + * @param int $entityNumber + * @param string $table + * @param array $bind + * @param array $fixtureMap + * + * @return void + */ + private function setNewBindValue($entityId, $entityNumber, $table, array &$bind, array $fixtureMap) + { + $attributes = $this->getAttributesMetadata(); + if (isset($attributes[$table])) { + // Process binding new value for eav attributes + foreach ($fixtureMap as $fixtureField => $fixture) { + if (!isset($attributes[$table][$fixtureField])) { + continue; + } + $attribute = $attributes[$table][$fixtureField]; + + if (isset($bind[$attribute['link_field']]) + && $bind[$attribute['link_field']] === $attribute[$attribute['link_field']] + ) { + $bind[$attribute['value_field']] = $this->getBindValue($fixture, $entityId, $entityNumber); + break; + } + } + } elseif (isset($this->customTableMap[$table])) { + foreach ($this->customTableMap[$table]['fields'] as $field => $fixtureField) { + $bind[$field] = $this->getFixtureValue($fixtureField, $entityId, $entityNumber, $fixtureMap); + } + } + } + + /** + * @param string $fixtureField + * @param int $entityId + * @param int $entityNumber + * @param array $fixtureMap + * @return mixed|string + */ + private function getFixtureValue($fixtureField, $entityId, $entityNumber, array $fixtureMap) + { + $fixture = isset($fixtureMap[$fixtureField]) ? $fixtureMap[$fixtureField] : null; + return $fixture ? $this->getBindValue($fixture, $entityId, $entityNumber) : ''; + } + + /** + * @param callable|mixed $fixture + * @param int $entityId + * @param int $entityNumber + * @return string + */ + private function getBindValue($fixture, $entityId, $entityNumber) + { + $bindValue = is_callable($fixture) + ? call_user_func($fixture, $entityId, $entityNumber) + : $fixture; + + return is_array($bindValue) ? array_shift($bindValue) : $bindValue; + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/ProductGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/ProductGenerator.php new file mode 100644 index 0000000000000..8f5b4b52f66be --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/ProductGenerator.php @@ -0,0 +1,335 @@ + function ($entityId, $entityIndex) {return 'Product Name' . $entityIndex}, + * 'sku' => function ($entityId, $entityIndex) {return 'Product Sku' . $entityIndex}, + * ] + * And optional parameters (by default will be populated with default values) + * [ + * 'attribute_set_id' => value or callback in format function ($entityId, $entityIndex) {return attribute_id} + * 'additional_attributes' => callback in format function ($entityId, $entityIndex) {return [attribute => value]} + * 'url_key' => callback in format function ($entityId, $entityIndex) {return url_key} + * 'website_ids' => callback in format function ($entityId, $entityIndex) {return [website_id]} + * 'status' => value or callback in format function ($entityId, $entityIndex) {return status} + * 'price' => value or callback in format function ($entityId, $entityIndex) {return price} + * 'description' => value or callback in format function ($entityId, $entityIndex) {return description} + * 'short_description' => value or callback in format function ($entityId, $entityIndex) {return short_description} + * 'category_ids' => callback in format function ($entityId, $entityIndex) {return category_ids} + * 'type_id' => value or callback in format function ($entityId, $entityIndex) {return type_id} + * 'meta_keyword' => value or callback in format function ($entityId, $entityIndex) {return meta_keyword} + * 'meta_title' => value or callback in format function ($entityId, $entityIndex) {return meta_title} + * ] + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProductGenerator +{ + /** + * @var \Magento\Catalog\Model\ProductFactory + */ + private $productFactory; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory + */ + private $categoryCollectionFactory; + + /** + * @var \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory + */ + private $urlRewriteFactory; + + /** + * @var \Magento\Store\Model\ResourceModel\Store\CollectionFactory + */ + private $storeCollectionFactory; + + /** + * @var array + */ + private $categories = []; + + /** + * @var array + */ + private $storesPerWebsite = []; + + /** + * @var EntityGeneratorFactory + */ + private $entityGeneratorFactory; + + /** + * @var \Magento\Store\Model\StoreManagerInterface + */ + private $storeManager; + + /** + * @var ProductTemplateGeneratorFactory + */ + private $productTemplateGeneratorFactory; + + /** + * @var array + */ + private $productUrlSuffix = []; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var array + */ + private $customTableMap; + + /** + * @param \Magento\Catalog\Model\ProductFactory $productFactory + * @param \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory + * @param \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory $urlRewriteFactory + * @param \Magento\Store\Model\ResourceModel\Store\CollectionFactory $storeCollectionFactory + * @param EntityGeneratorFactory $entityGeneratorFactory + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param ProductTemplateGeneratorFactory $productTemplateGeneratorFactory + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param array $customTableMap + */ + public function __construct( + \Magento\Catalog\Model\ProductFactory $productFactory, + \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory, + \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory $urlRewriteFactory, + \Magento\Store\Model\ResourceModel\Store\CollectionFactory $storeCollectionFactory, + EntityGeneratorFactory $entityGeneratorFactory, + \Magento\Store\Model\StoreManagerInterface $storeManager, + ProductTemplateGeneratorFactory $productTemplateGeneratorFactory, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + $customTableMap = [] + ) { + $this->productFactory = $productFactory; + $this->categoryCollectionFactory = $categoryCollectionFactory; + $this->urlRewriteFactory = $urlRewriteFactory; + $this->storeCollectionFactory = $storeCollectionFactory; + $this->entityGeneratorFactory = $entityGeneratorFactory; + $this->storeManager = $storeManager; + $this->productTemplateGeneratorFactory = $productTemplateGeneratorFactory; + $this->scopeConfig = $scopeConfig; + $this->customTableMap = $customTableMap; + } + + /** + * Generate simple products + * + * @param int $products + * @param array $fixtureMap + * @return void + */ + public function generate($products, $fixtureMap) + { + $this->initializeFixtureDefaultValues($fixtureMap); + $attributeSets = []; + // prepare attribute sets distribution for save products per attribute set + for ($productNumber = 1; $productNumber <= $products; $productNumber++) { + $attributeSetId = $this->getFixtureValue('attribute_set_id', $productNumber, $productNumber, $fixtureMap); + if (!isset($attributeSets[$attributeSetId])) { + $attributeSets[$attributeSetId] = 0; + } + $attributeSets[$attributeSetId]++; + } + + $customTableMap = [ + 'url_rewrite' => [ + 'entity_id_field' => 'entity_id', + 'handler' => function ($productId, $entityNumber, $fixture) { + return $this->urlRewriteHandler($productId, $entityNumber, $fixture); + }, + ], + 'catalog_category_product' => [ + 'fields' => [ + 'category_id' => 'category_ids', + ], + ], + 'catalog_product_entity' => [ + 'fields' => [ + 'attribute_set_id' => 'attribute_set_id', + 'sku' => 'sku', + ], + ], + ]; + if (count($fixtureMap['website_ids'](1, 0)) === 1) { + // Get website id from fixture in case when one site is assigned per product + $customTableMap['catalog_product_website'] = [ + 'fields' => [ + 'website_id' => 'website_ids', + ] + ]; + } + $generator = $this->entityGeneratorFactory->create( + [ + 'entityType' => ProductInterface::class, + 'customTableMap' => array_merge($customTableMap, $this->customTableMap) + ] + ); + foreach ($attributeSets as $attributeSetId => $productsAmount) { + $fixtureMap = array_merge($fixtureMap, ['attribute_set_id' => $attributeSetId]); + $generator->generate( + $this->productTemplateGeneratorFactory->create($fixtureMap), + $productsAmount, + function ($productNumber, $entityNumber) use ($attributeSetId, $fixtureMap) { + // add additional attributes to fixture for fulfill it during product generation + return array_merge( + $fixtureMap, + $fixtureMap['additional_attributes']($attributeSetId, $productNumber, $entityNumber) + ); + } + ); + } + } + + /** + * @param array $fixture + * @return void + */ + private function initializeFixtureDefaultValues(array &$fixture) + { + $defaultValues = [ + 'attribute_set_id' => function () { + return $this->productFactory->create()->getDefaultAttributeSetId(); + }, + 'additional_attributes' => function () { + return []; + }, + 'url_key' => function ($productId, $entityNumber) use ($fixture) { + return strtolower(str_replace(' ', '-', $fixture['sku']($productId, $entityNumber))); + }, + 'website_ids' => function () { + return $this->storeManager->getDefaultStoreView()->getWebsiteId(); + }, + 'status' => Status::STATUS_ENABLED, + ]; + foreach ($defaultValues as $fixtureKey => $value) { + if (!isset($fixture[$fixtureKey])) { + $fixture[$fixtureKey] = $value; + } + } + } + + /** + * @param string $fixtureKey + * @param int $productId + * @param int $entityNumber + * @param array $fixtureMap + * @return mixed|string + */ + private function getFixtureValue($fixtureKey, $productId, $entityNumber, $fixtureMap) + { + $fixtureValue = isset($fixtureMap[$fixtureKey]) ? $fixtureMap[$fixtureKey] : null; + return $fixtureValue ? $this->getBindValue($fixtureValue, $productId, $entityNumber) : ''; + } + + /** + * @param callable|mixed $fixtureValue + * @param int $productId + * @param int $entityNumber + * @return mixed + */ + private function getBindValue($fixtureValue, $productId, $entityNumber) + { + return is_callable($fixtureValue) + ? call_user_func($fixtureValue, $productId, $entityNumber) + : $fixtureValue; + } + + /** + * Handle generation sql query for url rewrite + * + * @param int $productId + * @param int $entityNumber + * @param array $fixtureMap + * @return array + */ + private function urlRewriteHandler($productId, $entityNumber, $fixtureMap) + { + $binds = []; + $websiteIds = $fixtureMap['website_ids']($productId, $entityNumber); + $websiteIds = is_array($websiteIds) ? $websiteIds : [$websiteIds]; + + $bindPerStore = []; + $requestPath = $this->getFixtureValue('url_key', $productId, $entityNumber, $fixtureMap); + $targetPath = 'catalog/product/view/id/' . $productId; + $urlRewrite = $this->urlRewriteFactory + ->create() + ->setRequestPath($requestPath) + ->setTargetPath($targetPath) + ->setEntityId($productId) + ->setEntityType('product'); + $binds[] = $urlRewrite->toArray(); + + if (isset($fixtureMap['category_ids'])) { + $categoryId = $fixtureMap['category_ids']($productId, $entityNumber); + if (!isset($this->categories[$categoryId])) { + $this->categories[$categoryId] = $this->categoryCollectionFactory + ->create() + ->addIdFilter($categoryId) + ->addAttributeToSelect('url_path') + ->getFirstItem() + ->getUrlPath(); + } + $urlRewrite->setMetadata(['category_id' => $categoryId]) + ->setRequestPath($this->categories[$categoryId] . '/' . $requestPath) + ->setTargetPath($targetPath . '/category/' . $categoryId); + $binds[] = $urlRewrite->toArray(); + } + + foreach ($websiteIds as $websiteId) { + if (!isset($this->storesPerWebsite[$websiteId])) { + $this->storesPerWebsite[$websiteId] = $this->storeCollectionFactory + ->create() + ->addWebsiteFilter($websiteId) + ->getAllIds(); + } + foreach ($binds as $bind) { + foreach ($this->storesPerWebsite[$websiteId] as $storeId) { + $bindWithStore = $bind; + $bindWithStore['store_id'] = $storeId; + $bindWithStore['request_path'] .= $this->getUrlSuffix($storeId); + $bindPerStore[] = $bindWithStore; + } + } + } + + return $bindPerStore; + } + + /** + * Get url suffix per store for product + * + * @param int $storeId + * @return string + */ + private function getUrlSuffix($storeId) + { + if (!isset($this->productUrlSuffix[$storeId])) { + $this->productUrlSuffix[$storeId] = $this->scopeConfig->getValue( + ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + return $this->productUrlSuffix[$storeId]; + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/ProductTemplateGeneratorFactory.php b/setup/src/Magento/Setup/Model/FixtureGenerator/ProductTemplateGeneratorFactory.php new file mode 100644 index 0000000000000..e364faaa08cc7 --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/ProductTemplateGeneratorFactory.php @@ -0,0 +1,58 @@ + SimpleProductTemplateGenerator::class, + BundleType::TYPE_CODE => BundleProductTemplateGenerator::class, + Configurable::TYPE_CODE => ConfigurableProductTemplateGenerator::class, + ]; + + /** + * @param ObjectManagerInterface $objectManager + */ + public function __construct(ObjectManagerInterface $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * @param array $fixture + * @return TemplateEntityGeneratorInterface + * @throws \InvalidArgumentException + */ + public function create(array $fixture) + { + $type = isset($fixture['type_id']) ? $fixture['type_id'] : Type::TYPE_SIMPLE; + if (!isset($this->templateEntityMap[$type])) { + throw new \InvalidArgumentException(sprintf( + 'Cannot instantiate product template generator. Wrong type_id "%s" passed', + $type + )); + } + + return $this->objectManager->create($this->templateEntityMap[$type], ['fixture' => $fixture]); + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/SimpleProductTemplateGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/SimpleProductTemplateGenerator.php new file mode 100644 index 0000000000000..5024b05b6e82b --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/SimpleProductTemplateGenerator.php @@ -0,0 +1,97 @@ +fixture = $fixture; + $this->productFactory = $productFactory; + } + + /** + * {@inheritdoc} + */ + public function generateEntity() + { + $attributeSet = $this->fixture['attribute_set_id']; + $product = $this->getProductTemplate( + $attributeSet, + $this->fixture['additional_attributes']($attributeSet, 0, 0) + ); + $product->save(); + + return $product; + } + + /** + * Get product template + * + * @param int $attributeSet + * @param array $additionalAttributes + * @return ProductInterface + */ + private function getProductTemplate($attributeSet, $additionalAttributes = []) + { + $productRandomizerNumber = crc32(mt_rand(1, PHP_INT_MAX)); + $product = $this->productFactory->create([ + 'data' => [ + 'attribute_set_id' => $attributeSet, + 'type_id' => Type::TYPE_SIMPLE, + 'name' => 'template name' . $productRandomizerNumber, + 'url_key' => 'template-url' . $productRandomizerNumber, + 'sku' => 'template_sku' . $productRandomizerNumber, + 'price' => 10, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'status' => Status::STATUS_ENABLED, + 'website_ids' => (array)$this->fixture['website_ids'](1, 0), + 'category_ids' => isset($this->fixture['category_ids']) ? [2] : null, + 'weight' => 1, + 'description' => 'description', + 'short_description' => 'short description', + 'tax_class_id' => 2, //'taxable goods', + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'qty' => 100500, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1 + ], + ] + ]); + + foreach ($additionalAttributes as $attributeCode => $attributeValue) { + $product->setData($attributeCode, $attributeValue); + } + + return $product; + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/SqlCollector.php b/setup/src/Magento/Setup/Model/FixtureGenerator/SqlCollector.php new file mode 100644 index 0000000000000..c94a89bede00f --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/SqlCollector.php @@ -0,0 +1,141 @@ +resourceConnection = $resourceConnection; + } + + /** + * @param string $sql + * @param array $bind + * @return void + */ + private function addSql($sql, $bind) + { + preg_match('~INSERT INTO `(.*)` \((.*)\) VALUES (\(.*\))+~', $sql, $queryMatches); + if ($queryMatches) { + $table = $queryMatches[1]; + $fields = preg_replace('~[\s+`]+~', '', $queryMatches[2]); + $fields = $fields ? explode(',', $fields) : []; + $sqlBindGroupAmount = count(explode('), (', $queryMatches[3])); + preg_match(' ~\((.*?)\)~', $queryMatches[3], $sqlBind); + $sqlBind = preg_replace(['~,\s*~', '~\'~'], [',', ''], $sqlBind[1]); + $sqlBind = $sqlBind ? explode(',', $sqlBind) : []; + $binds = []; + + // process multi queries + if ($sqlBindGroupAmount > 1) { + $valuesCount = count($bind)/$sqlBindGroupAmount; + for ($i = 0; $i < $sqlBindGroupAmount; $i++) { + $binds[] = array_combine( + $fields, + $this->handleBindValues($sqlBind, $bind, $i * $valuesCount) + ); + } + } else { + $sqlBind = $this->handleBindValues($sqlBind, $bind); + $binds[] = array_combine($fields, $sqlBind); + } + $this->sql[] = [$binds, $table]; + } + } + + /** + * @param array $sqlBind + * @param array $bind + * @param int $bindPosition + * @return array + */ + private function handleBindValues(array $sqlBind, array $bind, $bindPosition = 0) + { + $bind = array_values($bind); + foreach ($sqlBind as $i => $fieldValue) { + if ($fieldValue === '?') { + $sqlBind[$i] = $bind[$bindPosition]; + $bindPosition++; + } + } + + return $sqlBind; + } + + /** + * @return array + */ + public function getSql() + { + return $this->sql; + } + + /** + * Enable sql parsing + * + * @return void + */ + public function enable() + { + $this->sql = []; + $this->getProfiler()->clear(); + $this->getProfiler()->setEnabled(true); + } + + /** + * Disable sql parsing and collect all queries from profiler + * + * @return void + */ + public function disable() + { + $this->getProfiler()->setEnabled(false); + $queries = $this->getProfiler()->getQueryProfiles() ?: []; + foreach ($queries as $query) { + if ($query->getQueryType() === Profiler::INSERT) { + $this->addSql($query->getQuery(), $query->getQueryParams()); + } + } + } + + /** + * @return \Zend_Db_Profiler + */ + private function getProfiler() + { + if ($this->profiler === null) { + $this->profiler = $this->resourceConnection->getConnection()->getProfiler(); + } + + return $this->profiler; + } +} diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/TemplateEntityGeneratorInterface.php b/setup/src/Magento/Setup/Model/FixtureGenerator/TemplateEntityGeneratorInterface.php new file mode 100644 index 0000000000000..7d36948830756 --- /dev/null +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/TemplateEntityGeneratorInterface.php @@ -0,0 +1,18 @@ + self::COMPOSER_SHOW, - self::PARAM_PACKAGE => $package, - self::PARAM_AVAILABLE => true - ]; - - $applicationFactory = $this->objectManagerProvider->get() - ->get(\Magento\Framework\Composer\MagentoComposerApplicationFactory::class); - /** @var \Magento\Composer\MagentoComposerApplication $application */ - $application = $applicationFactory->create(); - - $result = $application->runComposerCommand($commandParams); - $matches = []; - preg_match($versionsPattern, $result, $matches); - if (isset($matches[1])) { - return explode(', ', $matches[1]); - } + } + + return $this->getAvailableVersionsFromAllRepositories($package); + } + + /** + * Get available versions of package by "composer show" command + * + * @param string $package + * @return array + * @exception \RuntimeException + */ + private function getAvailableVersionsFromAllRepositories($package) + { + $versionsPattern = '/^versions\s*\:\s(.+)$/m'; + + $commandParams = [ + self::PARAM_COMMAND => self::COMPOSER_SHOW, + self::PARAM_PACKAGE => $package, + self::PARAM_AVAILABLE => true + ]; + + $applicationFactory = $this->objectManagerProvider->get() + ->get(\Magento\Framework\Composer\MagentoComposerApplicationFactory::class); + /** @var \Magento\Composer\MagentoComposerApplication $application */ + $application = $applicationFactory->create(); + + $result = $application->runComposerCommand($commandParams); + $matches = []; + preg_match($versionsPattern, $result, $matches); + if (isset($matches[1])) { + return explode(', ', $matches[1]); } throw new \RuntimeException( diff --git a/setup/src/Magento/Setup/Model/PayloadValidator.php b/setup/src/Magento/Setup/Model/PayloadValidator.php index b024b028cb81f..6cab630ca7716 100644 --- a/setup/src/Magento/Setup/Model/PayloadValidator.php +++ b/setup/src/Magento/Setup/Model/PayloadValidator.php @@ -1,6 +1,6 @@ current) { - $this->current = array_map('strtolower', get_loaded_extensions()); + $this->current = array_map(function ($ext) { + return str_replace(' ', '-', strtolower($ext)); + }, get_loaded_extensions()); } return $this->current; } diff --git a/setup/src/Magento/Setup/Model/PhpReadinessCheck.php b/setup/src/Magento/Setup/Model/PhpReadinessCheck.php index 74b2afd1419c4..c593059edd4a6 100644 --- a/setup/src/Magento/Setup/Model/PhpReadinessCheck.php +++ b/setup/src/Magento/Setup/Model/PhpReadinessCheck.php @@ -1,6 +1,6 @@ descriptionGenerator = $descriptionGenerator; + $this->searchTermManager = $searchTermManager; + } + + /** + * Generate description with search terms + * + * @param int $currentProductIndex + * @return string + */ + public function generate($currentProductIndex) + { + $description = $this->getDescription(); + $this->searchTermManager->applySearchTermsToDescription($description, (int) $currentProductIndex); + + return $description; + } + + /** + * Generate new description or use cached one + * + * @param bool $useCachedDescription + * @return string + */ + private function getDescription($useCachedDescription = true) + { + if ($useCachedDescription !== true || $this->cachedDescription === null) { + $this->cachedDescription = $this->descriptionGenerator->generate(); + } + + return $this->cachedDescription; + } +} diff --git a/setup/src/Magento/Setup/Model/SearchTermDescriptionGeneratorFactory.php b/setup/src/Magento/Setup/Model/SearchTermDescriptionGeneratorFactory.php new file mode 100644 index 0000000000000..fed645e975ed1 --- /dev/null +++ b/setup/src/Magento/Setup/Model/SearchTermDescriptionGeneratorFactory.php @@ -0,0 +1,147 @@ +objectManager = $objectManager; + $this->fixtureConfig = $fixtureConfig; + } + + /** + * Search term description factory + * + * @param array|null $descriptionConfig + * @param array|null $searchTermsConfig + * @param int $totalProductsCount + * @param string $defaultDescription + * @return DescriptionGeneratorInterface + */ + public function create( + $descriptionConfig, + $searchTermsConfig, + $totalProductsCount, + $defaultDescription = '' + ) { + $this->updateSearchTermConfig($searchTermsConfig); + if (empty($descriptionConfig) || empty($searchTermsConfig)) { + return $this->objectManager->create( + DefaultDescriptionGenerator::class, + ['defaultDescription' => $defaultDescription] + ); + } + return $this->objectManager->create(\Magento\Setup\Model\SearchTermDescriptionGenerator::class, [ + 'descriptionGenerator' => $this->buildDescriptionGenerator($descriptionConfig), + 'searchTermManager' => $this->buildSearchTermManager($searchTermsConfig, $totalProductsCount) + ]); + } + + /** + * Update search terms distribution to be almost the same per each website + * + * @param array|null $searchTermsConfig + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @return void + */ + private function updateSearchTermConfig(&$searchTermsConfig) + { + if (null !== $searchTermsConfig) { + $websitesCount = (bool)$this->fixtureConfig->getValue('assign_entities_to_all_websites', false) + ? 1 + : (int)$this->fixtureConfig->getValue('websites', 1); + array_walk( + $searchTermsConfig, + function (&$searchTerm, $key, $websitesCount) { + $searchTerm['count'] *= $websitesCount; + }, + $websitesCount + ); + } + } + + /** + * Builder for DescriptionGenerator + * + * @param array $descriptionConfig + * @return \Magento\Setup\Model\Description\DescriptionGenerator + */ + private function buildDescriptionGenerator(array $descriptionConfig) + { + $sentenceGeneratorFactory = $this->objectManager->create( + \Magento\Setup\Model\Description\DescriptionSentenceGeneratorFactory::class + ); + $paragraphGeneratorFactory = $this->objectManager->create( + \Magento\Setup\Model\Description\DescriptionParagraphGeneratorFactory::class + ); + $descriptionGeneratorFactory = $this->objectManager->create( + \Magento\Setup\Model\Description\DescriptionGeneratorFactory::class + ); + $dictionaryFactory = $this->objectManager->create( + \Magento\Setup\Model\DictionaryFactory::class + ); + + $sentenceGenerator = $sentenceGeneratorFactory->create([ + 'dictionary' => $dictionaryFactory->create([ + 'dictionaryFilePath' => realpath(__DIR__ . '/../Fixtures/_files/dictionary.csv') + ]), + 'sentenceConfig' => $descriptionConfig['paragraphs']['sentences'] + ]); + + $paragraphGenerator = $paragraphGeneratorFactory->create([ + 'sentenceGenerator' => $sentenceGenerator, + 'paragraphConfig' => $descriptionConfig['paragraphs'] + ]); + + $descriptionGenerator = $descriptionGeneratorFactory->create([ + 'paragraphGenerator' => $paragraphGenerator, + 'mixinManager' => $this->objectManager->create(\Magento\Setup\Model\Description\MixinManager::class), + 'descriptionConfig' => $descriptionConfig + ]); + + return $descriptionGenerator; + } + + /** + * Builder for SearchTermManager + * + * @param array $searchTermsConfig + * @param int $totalProductsCount + * @return \Magento\Setup\Model\SearchTermManager + */ + private function buildSearchTermManager(array $searchTermsConfig, $totalProductsCount) + { + $searchTermManagerFactory = $this->objectManager->get( + \Magento\Setup\Model\SearchTermManagerFactory::class + ); + + return $searchTermManagerFactory->create([ + 'searchTerms' => $searchTermsConfig, + 'totalProductsCount' => $totalProductsCount + ]); + } +} diff --git a/setup/src/Magento/Setup/Model/SearchTermManager.php b/setup/src/Magento/Setup/Model/SearchTermManager.php new file mode 100644 index 0000000000000..5ca68757c8934 --- /dev/null +++ b/setup/src/Magento/Setup/Model/SearchTermManager.php @@ -0,0 +1,83 @@ +searchTerms = $searchTerms; + $this->totalProductsCount = (int) $totalProductsCount; + } + + /** + * Apply search terms to product description + * based on search terms use distribution + * + * @param string $description + * @param int $currentProductIndex + * @return void + */ + public function applySearchTermsToDescription(&$description, $currentProductIndex) + { + if ($this->searchTermsUseRate === null) { + $this->calculateSearchTermsUseRate(); + } + + foreach ($this->searchTerms as &$searchTerm) { + if ( + $this->searchTermsUseRate[$searchTerm['term']]['use_rate'] > 0 + && $currentProductIndex % $this->searchTermsUseRate[$searchTerm['term']]['use_rate'] === 0 + && $this->searchTermsUseRate[$searchTerm['term']]['used'] < $searchTerm['count'] + ) { + $description .= ' ' . $searchTerm['term']; + $this->searchTermsUseRate[$searchTerm['term']]['used'] += 1; + } + } + } + + /** + * Calculates search terms use distribution + * based on total amount of products that will be generated + * and number of each search term + * + * @return void; + */ + private function calculateSearchTermsUseRate() + { + foreach ($this->searchTerms as $searchTerm) { + $this->searchTermsUseRate[$searchTerm['term']] = [ + 'use_rate' => floor($this->totalProductsCount / $searchTerm['count']), + 'used' => 0 + ]; + } + } +} diff --git a/setup/src/Magento/Setup/Model/StoreConfigurationDataMapper.php b/setup/src/Magento/Setup/Model/StoreConfigurationDataMapper.php index 30e7411248e93..83b745e7945eb 100644 --- a/setup/src/Magento/Setup/Model/StoreConfigurationDataMapper.php +++ b/setup/src/Magento/Setup/Model/StoreConfigurationDataMapper.php @@ -1,6 +1,6 @@ classesScanner->getList($path) as $className) { try { - if (!strpos($path, 'generation')) { // validate all classes except classes in var/generation dir + // validate all classes except classes in generated/code dir + $generatedCodeDir = DirectoryList::getDefaultConfig()[DirectoryList::GENERATED_CODE]; + if (!strpos($path, $generatedCodeDir[DirectoryList::PATH])) { $this->validator->validate($className); } $nameList[] = $className; diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php index da4839f494f6a..5a2cb8703f1a5 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php @@ -1,6 +1,6 @@ isScanned = true; } -} \ No newline at end of file +} diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/Type.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/Type.php index e05fffd0623f0..fecd61e4747b3 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Reader/Type.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/Type.php @@ -1,7 +1,7 @@ initialize(); - $serialized = serialize($config); - file_put_contents($this->directoryList->getPath(DirectoryList::DI) . '/' . $key . '.ser', $serialized); + file_put_contents( + $this->directoryList->getPath(DirectoryList::GENERATED_METADATA) . '/' . $key . '.ser', + $this->getSerializer()->serialize($config) + ); } /** @@ -50,8 +58,23 @@ public function write($key, array $config) */ private function initialize() { - if (!file_exists($this->directoryList->getPath(DirectoryList::DI))) { - mkdir($this->directoryList->getPath(DirectoryList::DI)); + if (!file_exists($this->directoryList->getPath(DirectoryList::GENERATED_METADATA))) { + mkdir($this->directoryList->getPath(DirectoryList::GENERATED_METADATA)); + } + } + + /** + * Get serializer + * + * @return SerializerInterface + * @deprecated + */ + private function getSerializer() + { + if (null === $this->serializer) { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() + ->get(Serialize::class); } + return $this->serializer; } } diff --git a/setup/src/Magento/Setup/Module/Di/Compiler/Config/WriterInterface.php b/setup/src/Magento/Setup/Module/Di/Compiler/Config/WriterInterface.php index 79e7272099704..8a1f28f62e0ff 100644 --- a/setup/src/Magento/Setup/Module/Di/Compiler/Config/WriterInterface.php +++ b/setup/src/Magento/Setup/Module/Di/Compiler/Config/WriterInterface.php @@ -1,7 +1,7 @@ _fileHandler); + if (is_resource($this->_fileHandler)) { + fclose($this->_fileHandler); + } + } + + /** + * Destructor for closing file handler + */ + public function __destruct() + { + if (is_resource($this->_fileHandler)) { + fclose($this->_fileHandler); + } } } diff --git a/setup/src/Magento/Setup/Module/I18n/Dictionary/Writer/Csv/Stdo.php b/setup/src/Magento/Setup/Module/I18n/Dictionary/Writer/Csv/Stdo.php index 2b75bf83b4a34..6f8be9c4c2789 100644 --- a/setup/src/Magento/Setup/Module/I18n/Dictionary/Writer/Csv/Stdo.php +++ b/setup/src/Magento/Setup/Module/I18n/Dictionary/Writer/Csv/Stdo.php @@ -1,6 +1,6 @@ getApplication(); $serviceManager = $application->getServiceManager(); + if ($serviceManager->get(\Magento\Framework\App\DeploymentConfig::class)->isAvailable()) { /** @var \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider */ $objectManagerProvider = $serviceManager->get(\Magento\Setup\Model\ObjectManagerProvider::class); @@ -135,17 +136,26 @@ public function authPreDispatch($event) 'appState' => $adminAppState ] ); - if (!$objectManager->get(\Magento\Backend\Model\Auth::class)->isLoggedIn()) { + /** @var \Magento\Backend\Model\Auth $auth */ + $authentication = $objectManager->get(\Magento\Backend\Model\Auth::class); + + if ( + !$authentication->isLoggedIn() || + !$adminSession->isAllowed('Magento_Backend::setup_wizard') + ) { $adminSession->destroy(); + /** @var \Zend\Http\Response $response */ $response = $event->getResponse(); $baseUrl = Http::getDistroBaseUrlPath($_SERVER); $response->getHeaders()->addHeaderLine('Location', $baseUrl . 'index.php/session/unlogin'); $response->setStatusCode(302); $event->stopPropagation(); + return $response; } } } + return false; } diff --git a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php index 027c3f8d36233..cebca17dfd571 100644 --- a/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php +++ b/setup/src/Magento/Setup/Mvc/View/Http/InjectTemplateListener.php @@ -1,6 +1,6 @@ fixtureModel->expects($this->once())->method('loadConfig')->with('path_to_profile.xml'); $this->fixtureModel->expects($this->once())->method('initObjectManager'); $this->fixtureModel->expects($this->once())->method('loadFixtures'); - + $commandTester = new CommandTester($this->command); $commandTester->execute(['profile' => 'path_to_profile.xml']); } /** * @expectedException \RuntimeException - * @expectedExceptionMessage Not enough arguments. + * @expectedExceptionMessage Not enough arguments */ public function testExecuteInvalidLanguageArgument() { diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoAdminUriCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoAdminUriCommandTest.php index f63c8cc8fcf59..0d0c3b9c3efd5 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoAdminUriCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/InfoAdminUriCommandTest.php @@ -1,6 +1,6 @@ urlValidatorMock = $this->getMock(UrlValidator::class, [], [], '', false); + $this->localeValidatorMock = $this->getMock(LocaleValidator::class, [], [], '', false); + $this->timezoneValidatorMock = $this->getMock(TimezoneValidator::class, [], [], '', false); + $this->currencyValidatorMock = $this->getMock(CurrencyValidator::class, [], [], '', false); + $this->installerFactory = $this->getMock(\Magento\Setup\Model\InstallerFactory::class, [], [], '', false); $this->deploymentConfig = $this->getMock(\Magento\Framework\App\DeploymentConfig::class, [], [], '', false); $this->installer = $this->getMock(\Magento\Setup\Model\Installer::class, [], [], '', false); @@ -62,7 +93,11 @@ protected function setUp() $this->command = new InstallStoreConfigurationCommand( $this->installerFactory, $this->deploymentConfig, - $objectManagerProvider + $objectManagerProvider, + $this->localeValidatorMock, + $this->timezoneValidatorMock, + $this->currencyValidatorMock, + $this->urlValidatorMock ); } @@ -102,41 +137,11 @@ public function testExecuteNotInstalled() */ public function testExecuteInvalidData(array $option, $error) { - $url= $this->getMock(\Magento\Framework\Url\Validator::class, [], [], '', false); - $url->expects($this->any())->method('isValid')->will($this->returnValue(false)); - if (!isset($option['--' . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE])) { - $url->expects($this->any())->method('getMessages')->will($this->returnValue([ - Validator::INVALID_URL => 'Invalid URL.' - ])); - } - $localeLists= $this->getMock(\Magento\Framework\Validator\Locale::class, [], [], '', false); - $localeLists->expects($this->any())->method('isValid')->will($this->returnValue(false)); - $timezoneLists= $this->getMock(\Magento\Framework\Validator\Timezone::class, [], [], '', false); - $timezoneLists->expects($this->any())->method('isValid')->will($this->returnValue(false)); - $currencyLists= $this->getMock(\Magento\Framework\Validator\Currency::class, [], [], '', false); - $currencyLists->expects($this->any())->method('isValid')->will($this->returnValue(false)); - - $returnValueMapOM = [ - [ - \Magento\Framework\Url\Validator::class, - $url - ], - [ - \Magento\Framework\Validator\Locale::class, - $localeLists - ], - [ - \Magento\Framework\Validator\Timezone::class, - $timezoneLists - ], - [ - \Magento\Framework\Validator\Currency::class, - $currencyLists - ], - ]; - $this->objectManager->expects($this->any()) - ->method('get') - ->will($this->returnValueMap($returnValueMapOM)); + $this->localeValidatorMock->expects($this->any())->method('isValid')->willReturn(false); + $this->timezoneValidatorMock->expects($this->any())->method('isValid')->willReturn(false); + $this->currencyValidatorMock->expects($this->any())->method('isValid')->willReturn(false); + $this->urlValidatorMock->expects($this->any())->method('isValid')->willReturn(false); + $this->deploymentConfig->expects($this->once()) ->method('isAvailable') ->will($this->returnValue(true)); @@ -144,7 +149,7 @@ public function testExecuteInvalidData(array $option, $error) ->method('create'); $commandTester = new CommandTester($this->command); $commandTester->execute($option); - $this->assertEquals($error . PHP_EOL, $commandTester->getDisplay()); + $this->assertContains($error, $commandTester->getDisplay()); } /** @@ -155,48 +160,54 @@ public function validateDataProvider() return [ [ ['--' . StoreConfigurationDataMapper::KEY_BASE_URL => 'sampleUrl'], - 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL . '\': Invalid URL.' + 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL . '\': Invalid URL \'sampleUrl\'.' + ], + [ + ['--' . StoreConfigurationDataMapper::KEY_BASE_URL => 'http://example.com_test'], + 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL + . '\': Invalid URL \'http://example.com_test\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_LANGUAGE => 'sampleLanguage'], 'Command option \'' . StoreConfigurationDataMapper::KEY_LANGUAGE - . '\': Invalid value. To see possible values, run command \'bin/magento info:language:list\'.' + . '\': Invalid value. To see possible values, run command \'bin/magento info:language:list\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_TIMEZONE => 'sampleTimezone'], 'Command option \'' . StoreConfigurationDataMapper::KEY_TIMEZONE - . '\': Invalid value. To see possible values, run command \'bin/magento info:timezone:list\'.' + . '\': Invalid value. To see possible values, run command \'bin/magento info:timezone:list\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_CURRENCY => 'sampleLanguage'], 'Command option \'' . StoreConfigurationDataMapper::KEY_CURRENCY - . '\': Invalid value. To see possible values, run command \'bin/magento info:currency:list\'.' + . '\': Invalid value. To see possible values, run command \'bin/magento info:currency:list\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_USE_SEF_URL => 'invalidValue'], 'Command option \'' . StoreConfigurationDataMapper::KEY_USE_SEF_URL - . '\': Invalid value. Possible values (0|1).' + . '\': Invalid value. Possible values (0|1).' ], [ ['--' . StoreConfigurationDataMapper::KEY_IS_SECURE => 'invalidValue'], 'Command option \'' . StoreConfigurationDataMapper::KEY_IS_SECURE - . '\': Invalid value. Possible values (0|1).' + . '\': Invalid value. Possible values (0|1).' ], [ ['--' . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE => 'http://www.sample.com'], 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE - . '\': Invalid secure URL.' + . '\': Invalid URL \'http://www.sample.com\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_IS_SECURE_ADMIN => 'invalidValue'], 'Command option \'' . StoreConfigurationDataMapper::KEY_IS_SECURE_ADMIN - . '\': Invalid value. Possible values (0|1).' + . '\': Invalid value. Possible values (0|1).' ], [ ['--' . StoreConfigurationDataMapper::KEY_ADMIN_USE_SECURITY_KEY => 'invalidValue'], 'Command option \'' . StoreConfigurationDataMapper::KEY_ADMIN_USE_SECURITY_KEY - . '\': Invalid value. Possible values (0|1).' + . '\': Invalid value. Possible values (0|1).' ], + ]; } } diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/MaintenanceAllowIpsCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/MaintenanceAllowIpsCommandTest.php index 126cbadb6a53b..7e6cd17b2639b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/MaintenanceAllowIpsCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/MaintenanceAllowIpsCommandTest.php @@ -1,6 +1,6 @@ getMock(\Magento\Setup\Model\InstallerFactory::class, [], [], '', false); $installer = $this->getMock(\Magento\Setup\Model\Installer::class, [], [], '', false); @@ -20,6 +25,25 @@ public function testExecute() $installer->expects($this->at(2))->method('installDataFixtures'); $installerFactory->expects($this->once())->method('create')->willReturn($installer); $commandTester = new CommandTester(new UpgradeCommand($installerFactory)); - $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->execute([])); + $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->execute($options)); + $this->assertEquals($expectedString, $commandTester->getDisplay()); + } + + /** + * @return array + */ + public function executeDataProvider() + { + return [ + [ + 'options' => [], + 'expectedString' => 'Please re-run Magento compile command. Use the command "setup:di:compile"' + . PHP_EOL + ], + [ + 'options' => ['--keep-generated' => true], + 'expectedString' => '' + ], + ]; } } diff --git a/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php b/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php index ce13e6977e773..2cb2ecf840f5f 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/CommandListTest.php @@ -1,6 +1,6 @@ serviceManagerMock = $this->getMockBuilder(\Zend\ServiceManager\ServiceManager::class) + $this->serviceManagerMock = $this->getMockBuilder(ServiceManager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->inputMock = $this->getMockBuilder(ArgvInput::class) ->disableOriginalConstructor() ->getMock(); - $this->inputMock = $this->getMockBuilder(\Symfony\Component\Console\Input\ArgvInput::class) + $this->filesystemDriverMock = $this->getMockBuilder(File::class) ->disableOriginalConstructor() ->getMock(); - $this->filesystemDriverMock = $this->getMockBuilder(\Magento\Framework\Filesystem\Driver\File::class) + $this->generationDirectoryAccessMock = $this->getMockBuilder(GenerationDirectoryAccess::class) ->disableOriginalConstructor() ->getMock(); + $this->model = (new ObjectManager($this))->getObject( - \Magento\Setup\Console\CompilerPreparation::class, + CompilerPreparation::class, [ 'serviceManager' => $this->serviceManagerMock, 'input' => $this->inputMock, - 'filesystemDriver' => $this->filesystemDriverMock + 'filesystemDriver' => $this->filesystemDriverMock, + 'generationDirectoryAccess' => $this->generationDirectoryAccessMock, ] ); } @@ -56,24 +80,37 @@ public function setUp() */ public function testClearGenerationDirWhenNeeded($commandName, $isCompileCommand, $isHelpOption, $dirExists = false) { - $this->inputMock->expects($this->once())->method('getFirstArgument')->willReturn($commandName); + $this->inputMock->expects($this->once()) + ->method('getFirstArgument') + ->willReturn($commandName); $this->inputMock->expects($this->atLeastOnce()) ->method('hasParameterOption') - ->with( - $this->logicalOr('--help', '-h') - )->willReturn($isHelpOption); + ->with($this->logicalOr('--help', '-h')) + ->willReturn($isHelpOption); + if ($isCompileCommand && !$isHelpOption) { $this->filesystemDriverMock->expects($this->exactly(2)) ->method('isExists') ->willReturn($dirExists); - $this->filesystemDriverMock->expects($this->exactly(((int)$dirExists) * 2))->method('deleteDirectory'); + $this->filesystemDriverMock->expects($this->exactly(((int)$dirExists) * 2)) + ->method('deleteDirectory'); } else { - $this->filesystemDriverMock->expects($this->never())->method('isExists'); - $this->filesystemDriverMock->expects($this->never())->method('deleteDirectory'); + $this->filesystemDriverMock->expects($this->never()) + ->method('isExists'); + $this->filesystemDriverMock->expects($this->never()) + ->method('deleteDirectory'); } + + $this->generationDirectoryAccessMock->expects($this->any()) + ->method('check') + ->willReturn(true); + $this->model->handleCompilerEnvironment(); } + /** + * @return array + */ public function commandNameDataProvider() { return [ @@ -108,10 +145,6 @@ public function testGenerationDirectoryFromInitParams() $customGenerationDirectory = '/custom/generated/code/directory'; $defaultDiDirectory = '/custom/di/directory'; $mageInitParams = ['MAGE_DIRS' => ['generation' => ['path' => $customGenerationDirectory]]]; - - $this->inputMock->expects($this->once()) - ->method('getFirstArgument') - ->willReturn(DiCompileCommand::NAME); $dirValueMap = [ [ $customGenerationDirectory, @@ -122,16 +155,24 @@ public function testGenerationDirectoryFromInitParams() true ] ]; - // Filesystem mock - $this->filesystemDriverMock->expects($this->exactly(2))->method('isExists')->willReturn(true); + + $this->inputMock->expects($this->once()) + ->method('getFirstArgument') + ->willReturn(DiCompileCommand::NAME); + $this->filesystemDriverMock->expects($this->exactly(2)) + ->method('isExists') + ->willReturn(true); $this->filesystemDriverMock->expects($this->exactly(2)) ->method('deleteDirectory') - ->will($this->returnValueMap($dirValueMap)); - + ->willReturnMap($dirValueMap); $this->serviceManagerMock->expects($this->once()) ->method('get') ->with(InitParamListener::BOOTSTRAP_PARAM) ->willReturn($mageInitParams); + $this->generationDirectoryAccessMock->expects($this->once()) + ->method('check') + ->willReturn(true); + $this->model->handleCompilerEnvironment(); } @@ -139,10 +180,6 @@ public function testGenerationDirectoryFromCliOption() { $customGenerationDirectory = '/custom/generated/code/directory'; $customDiDirectory = '/custom/di/directory'; - - $this->inputMock->expects($this->once()) - ->method('getFirstArgument') - ->willReturn(DiCompileCommand::NAME); $dirResultMap = [ [ $this->logicalNot($this->equalTo($customGenerationDirectory)), @@ -154,10 +191,18 @@ public function testGenerationDirectoryFromCliOption() ] ]; - $this->filesystemDriverMock->expects($this->exactly(2))->method('isExists')->willReturn(true); + $this->inputMock->expects($this->once()) + ->method('getFirstArgument') + ->willReturn(DiCompileCommand::NAME); + $this->filesystemDriverMock->expects($this->exactly(2)) + ->method('isExists') + ->willReturn(true); $this->filesystemDriverMock->expects($this->exactly(2)) ->method('deleteDirectory') - ->will($this->returnValueMap($dirResultMap)); + ->willReturnMap($dirResultMap); + $this->generationDirectoryAccessMock->expects($this->once()) + ->method('check') + ->willReturn(true); $this->model->handleCompilerEnvironment(); } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php index 64c431d946ea2..4673cff9dd291 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/AddDatabaseTest.php @@ -1,6 +1,6 @@ getMockBuilder(UrlValidator::class) + ->disableOriginalConstructor() + ->getMock(); + $validator->expects($this->any()) + ->method('isValid') + ->willReturnMap($returnMap); + + /** @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject $requestMock */ + $requestMock = $this->getMockBuilder(RequestInterface::class) + ->getMockForAbstractClass(); + $requestMock->expects($this->once()) + ->method('getContent') + ->willReturn(json_encode($requestJson)); + + $controller = $objectManagerHelper->getObject( + UrlCheck::class, + ['urlValidator' => $validator] + ); + $objectManagerHelper->setBackwardCompatibleProperty($controller, 'request', $requestMock); + + $this->assertEquals(new JsonModel($expectedResult), $controller->indexAction()); + } + + /** + * @return array + */ + public function indexActionDataProvider() + { + return [ + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost' + ] + ], + 'expectedResult' => ['successUrl' => true, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com_test' + ] + ], + 'expectedResult' => ['successUrl' => false, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com_test' + ], + 'https' => [ + 'admin' => false, + 'front' => false, + 'text' => '' + ] + ], + 'expectedResult' => ['successUrl' => false, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com:8080' + ], + 'https' => [ + 'admin' => true, + 'front' => false, + 'text' => 'https://example.com.ua/' + ] + ], + 'expectedResult' => ['successUrl' => true, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com:8080/folder_name/' + ], + 'https' => [ + 'admin' => false, + 'front' => true, + 'text' => 'https://example.com.ua/' + ] + ], + 'expectedResult' => ['successUrl' => true, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com:8080/folder_name/' + ], + 'https' => [ + 'admin' => true, + 'front' => true, + 'text' => 'https://example.com.ua:8090/folder_name/' + ] + ], + 'expectedResult' => ['successUrl' => true, 'successSecureUrl' => true] + ], + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php index 829c64651e346..b6b5fc6ae6608 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/WebConfigurationTest.php @@ -1,6 +1,6 @@ 'attribute set name', + 'attributes' => [ + 'attribute' => [ + [ + 'is_required' => 1, + 'is_visible_on_front' => 1, + 'is_visible_in_advanced_search' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'attribute_code' => 'attribute_1', + 'is_searchable' => 0, + 'frontend_label' => 'Attribute 1', + 'frontend_input' => 'select', + 'backend_type' => 1, + 'default_option' => 'option 1', + 'options' => [ + 'option' => [ + [ + 'label' => 'option 1', + 'value' => 'option_1' + ], + ] + ] + ] + ] + ] + ]; + + // Mock Attribute Sets + $attributeSetMock = $this->getMockBuilder(\Magento\Eav\Api\Data\AttributeSetInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeSetMock->expects($this->once()) + ->method('setAttributeSetName') + ->with("attribute set name"); + $attributeSetMock->expects($this->once()) + ->method('setEntityTypeId') + ->with(\Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE); + $attributeSetMock->expects($this->any()) + ->method('getAttributeSetName') + ->willReturn($attributeSets['name']); + + $attributeSetFactoryMock = $this->getMockBuilder(\Magento\Eav\Api\Data\AttributeSetInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $attributeSetFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($attributeSetMock); + + $attributeSetManagementMock = $this->getMockBuilder(\Magento\Catalog\Api\AttributeSetManagementInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeSetManagementMock->expects($this->once()) + ->method('create') + ->with($attributeSetMock, '4') + ->willReturn($attributeSetMock); + + //Mock Attribute Groups + $attributeGroupMock = $this->getMockBuilder(\Magento\Eav\Api\Data\AttributeGroupInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeGroupMock->expects($this->once()) + ->method('setAttributeGroupName') + ->with($attributeSetMock->getAttributeSetName() . ' - Group'); + $attributeGroupMock->expects($this->once()) + ->method('setAttributeSetId') + ->with($attributeSetMock->getAttributeSetId()); + + $attributeGroupFactoryMock = $this->getMockBuilder(\Magento\Eav\Api\Data\AttributeGroupInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $attributeGroupFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($attributeGroupMock); + + $productAttributeGroupRepoMock = $this->getMockBuilder( + \Magento\Catalog\Api\ProductAttributeGroupRepositoryInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + $productAttributeGroupRepoMock->expects($this->once()) + ->method('save') + ->with($attributeGroupMock) + ->willReturn($attributeGroupMock); + + // Mock Attributes + $attributeMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductAttributeInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $attributeFactoryMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductAttributeInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $attributeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($attributeMock); + + //Mock Attribute Options + $optionMock = $this->getMockBuilder(\Magento\Eav\Api\Data\AttributeOptionInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $optionFactoryMock = $this->getMockBuilder(\Magento\Eav\Api\Data\AttributeOptionInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $optionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($optionMock); + + $productAttributeRepoMock = $this->getMockBuilder( + \Magento\Catalog\Api\ProductAttributeRepositoryInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + $productAttributeRepoMock->expects($this->once()) + ->method('save') + ->with($attributeMock) + ->willReturn($attributeMock); + + $productAttributeManagementMock = $this->getMockBuilder( + \Magento\Catalog\Api\ProductAttributeManagementInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + $productAttributeManagementMock->expects($this->once()) + ->method('assign') + ->willReturn($attributeMock->getAttributeId()); + + $attributeSet = new \Magento\Setup\Fixtures\AttributeSet\AttributeSetFixture( + $attributeSetManagementMock, + $productAttributeGroupRepoMock, + $productAttributeRepoMock, + $productAttributeManagementMock, + $attributeFactoryMock, + $optionFactoryMock, + $attributeSetFactoryMock, + $attributeGroupFactoryMock + ); + $attributeSet->createAttributeSet($attributeSets); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php new file mode 100644 index 0000000000000..e17b9071a2605 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php @@ -0,0 +1,62 @@ + 'attribute set name', + 'attributes' => [ + 'attribute' => [ + [ + 'is_required' => 1, + 'is_visible_on_front' => 1, + 'is_visible_in_advanced_search' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'attribute_code' => 'attribute_1', + 'is_searchable' => 0, + 'frontend_label' => 'Attribute 1', + 'frontend_input' => 'select', + 'backend_type' => 1, + 'default_option' => 'option 1', + 'options' => [ + 'option' => [ + [ + 'label' => 'option 1', + 'value' => 'option_1' + ], + [ + 'label' => 'option 2', + 'value' => 'option_2' + ] + ] + ] + ] + ] + ] + ]; + $pattern = new \Magento\Setup\Fixtures\AttributeSet\Pattern(); + $this->assertEquals( + $attributeSets, + $pattern->generateAttributeSet( + 'attribute set name', + 1, + 2, + function ($index, $attributeData) { + $attributeData['backend_type'] = $index; + return $attributeData; + } + ) + ); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/SwatchesGeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/SwatchesGeneratorTest.php new file mode 100644 index 0000000000000..89bd0efdf267d --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/SwatchesGeneratorTest.php @@ -0,0 +1,73 @@ +getMockBuilder(Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + + // Mock Media Config + $mediaConfigMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + + // Mock Swatch Media Helper + $swatchHelperMock = $this->getMockBuilder(Media::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->swatchesGeneratorMock = new SwatchesGenerator( + $filesystemMock, + $mediaConfigMock, + $swatchHelperMock + ); + } + + public function testGenerateSwatchData() + { + + $attribute['swatch_input_type'] = Swatch::SWATCH_INPUT_TYPE_VISUAL; + $attribute['swatchvisual']['value'] = array_reduce( + range(1, 3), + function ($values, $index) { + $values['option_' . $index] = '#' . str_repeat(dechex(255 * $index / 3), 3); + return $values; + }, + [] + ); + + $attribute['optionvisual']['value'] = array_reduce( + range(1, 3), + function ($values, $index) { + $values['option_' . $index] = ['option ' . $index]; + return $values; + }, + [] + ); + + $this->assertEquals( + $attribute, + $this->swatchesGeneratorMock->generateSwatchData(3, 'test', 'color') + ); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSetsFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSetsFixtureTest.php new file mode 100644 index 0000000000000..e9c4142fb813f --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSetsFixtureTest.php @@ -0,0 +1,112 @@ +fixtureModelMock = $this->getMockBuilder(\Magento\Setup\Fixtures\FixtureModel::class) + ->disableOriginalConstructor() + ->getMock(); + $this->attributeSetsFixtureMock = $this->getMockBuilder(AttributeSetFixture::class) + ->disableOriginalConstructor() + ->getMock(); + $this->patternMock = $this->getMockBuilder(\Magento\Setup\Fixtures\AttributeSet\Pattern::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new AttributeSetsFixture( + $this->fixtureModelMock, + $this->attributeSetsFixtureMock, + $this->patternMock + ); + } + + public function testCreateAttributeSet() + { + $valueMap = [ + ['attribute_sets', null, ['attribute_set' => [['some-data']]]], + ['product_attribute_sets', null, null], + ]; + + $this->attributeSetsFixtureMock->expects($this->once()) + ->method('createAttributeSet') + ->with(['some-data']); + $this->fixtureModelMock + ->expects($this->exactly(2)) + ->method('getValue') + ->will($this->returnValueMap($valueMap)); + + $this->model->execute(); + } + + public function testCreateProductAttributeSet() + { + $valueMap = [ + ['attribute_sets', null, null], + ['product_attribute_sets', null, 1], + ['product_attribute_sets_attributes', 3, 2], + ['product_attribute_sets_attributes_values', 3, 3], + ]; + + $closure = function () { + }; + $this->patternMock->expects($this->once()) + ->method('generateAttributeSet') + ->with(\Magento\Setup\Fixtures\AttributeSetsFixture::PRODUCT_SET_NAME . 1, 2, 3, $closure) + ->willReturn(['some-data']); + $this->attributeSetsFixtureMock->expects($this->once()) + ->method('createAttributeSet') + ->with(['some-data']); + $this->fixtureModelMock + ->expects($this->exactly(4)) + ->method('getValue') + ->will($this->returnValueMap($valueMap)); + + $this->model->execute(); + } + + public function testGetActionTitle() + { + $this->assertSame('Generating attribute sets', $this->model->getActionTitle()); + } + + public function testIntroduceParamLabels() + { + $this->assertSame([ + 'attribute_sets' => 'Attribute Sets (Default)', + 'product_attribute_sets' => 'Attribute Sets (Extra)' + ], $this->model->introduceParamLabels()); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/CartPriceRulesFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/CartPriceRulesFixtureTest.php index 9a871cbd196d2..c8eb60180e843 100755 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/CartPriceRulesFixtureTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/CartPriceRulesFixtureTest.php @@ -1,6 +1,6 @@ fixtureModelMock = $this->getMock(\Magento\Setup\Fixtures\FixtureModel::class, [], [], '', false); + $this->fixtureModelMock = $this->getMock(FixtureModel::class, [], [], '', false); + $this->collectionFactoryMock = $this->getMock(CollectionFactory::class, ['create'], [], '', false); + $this->collectionMock = $this->getMock(Collection::class, [], [], '', false); + $this->categoryFactoryMock = $this->getMock(CategoryFactory::class, ['create'], [], '', false); + + $this->model = (new ObjectManager($this))->getObject(CategoriesFixture::class, [ + 'fixtureModel' => $this->fixtureModelMock, + 'collectionFactory' => $this->collectionFactoryMock, + 'rootCategoriesIds' => [2], + 'categoryFactory' => $this->categoryFactoryMock, + 'firstLevelCategoryIndex' => 1, + ]); + } + + public function testDoNoExecuteIfCategoriesAlreadyGenerated() + { + $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($this->collectionMock); + $this->collectionMock->expects($this->once())->method('getSize')->willReturn(32); + $this->fixtureModelMock + ->expects($this->once()) + ->method('getValue') + ->willReturn(30); + $this->categoryFactoryMock->expects($this->never())->method('create'); - $this->model = new CategoriesFixture($this->fixtureModelMock); + $this->model->execute(); } + public function testExecute() { - $categoryMock = $this->getMock( + $valueMap = [ + ['categories', 0, 1], + ['categories_nesting_level', 3, 3] + ]; + + $this->fixtureModelMock + ->expects($this->exactly(2)) + ->method('getValue') + ->will($this->returnValueMap($valueMap)); + + $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($this->collectionMock); + $this->collectionMock->expects($this->once())->method('getSize')->willReturn(2); + + $parentCategoryMock = $this->getMock( \Magento\Catalog\Model\Category::class, [ 'getName', 'setId', + 'getId', 'setUrlKey', 'setUrlPath', 'setName', 'setParentId', 'setPath', 'setLevel', + 'getLevel', 'setAvailableSortBy', 'setDefaultSortBy', 'setIsActive', - 'save' + 'setIsAnchor', + 'save', + 'setStoreId', + 'load', ], [], '', false ); + $parentCategoryMock->expects($this->once())->method('getId')->willReturn(5); + $parentCategoryMock->expects($this->once())->method('getLevel')->willReturn(3); + $categoryMock = clone $parentCategoryMock; $categoryMock->expects($this->once()) ->method('getName') + ->with('Category 1') ->will($this->returnValue('category_name')); $categoryMock->expects($this->once()) ->method('setId') @@ -65,12 +130,18 @@ public function testExecute() ->willReturnSelf(); $categoryMock->expects($this->once()) ->method('setParentId') + ->with(5) ->willReturnSelf(); $categoryMock->expects($this->once()) ->method('setPath') ->willReturnSelf(); + $categoryMock->expects($this->once()) + ->method('setIsAnchor') + ->with(true) + ->willReturnSelf(); $categoryMock->expects($this->once()) ->method('setLevel') + ->with(4) ->willReturnSelf(); $categoryMock->expects($this->once()) ->method('setAvailableSortBy') @@ -82,62 +153,7 @@ public function testExecute() ->method('setIsActive') ->willReturnSelf(); - $groupMock = $this->getMock(\Magento\Store\Model\Group::class, [], [], '', false); - $groupMock->expects($this->once()) - ->method('getRootCategoryId') - ->will($this->returnValue('root_category_id')); - - $storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManager::class, [], [], '', false); - $storeManagerMock->expects($this->once()) - ->method('getGroups') - ->will($this->returnValue([$groupMock])); - - $objectValueMock = [ - [\Magento\Store\Model\StoreManager::class, [], $storeManagerMock], - [\Magento\Catalog\Model\Category::class, [], $categoryMock] - ]; - - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->exactly(2)) - ->method('create') - ->will($this->returnValueMap($objectValueMock)); - - $valueMap = [ - ['categories', 0, 1], - ['categories_nesting_level', 3, 3] - ]; - - $this->fixtureModelMock - ->expects($this->exactly(2)) - ->method('getValue') - ->will($this->returnValueMap($valueMap)); - $this->fixtureModelMock - ->expects($this->exactly(2)) - ->method('getObjectManager') - ->will($this->returnValue($objectManagerMock)); - - $this->model->execute(); - } - - public function testNoFixtureConfigValue() - { - $categoryMock = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); - $categoryMock->expects($this->never())->method('save'); - - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->never()) - ->method('create') - ->with($this->equalTo(\Magento\Catalog\Model\Category::class)) - ->willReturn($categoryMock); - - $this->fixtureModelMock - ->expects($this->never()) - ->method('getObjectManager') - ->willReturn($objectManagerMock); - $this->fixtureModelMock - ->expects($this->once()) - ->method('getValue') - ->willReturn(false); + $this->categoryFactoryMock->expects($this->once())->method('create')->willReturn($categoryMock); $this->model->execute(); } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/ConfigsApplyFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/ConfigsApplyFixtureTest.php index aed093dad90c5..c917188fff2e2 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/ConfigsApplyFixtureTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/ConfigsApplyFixtureTest.php @@ -1,6 +1,6 @@ fixtureModelMock = $this->getMock(\Magento\Setup\Fixtures\FixtureModel::class, [], [], '', false); - - $this->model = new ConfigurableProductsFixture($this->fixtureModelMock); + $this->fixtureModelMock = $this->getMockBuilder(\Magento\Setup\Fixtures\FixtureModel::class) + ->disableOriginalConstructor() + ->setMethods(['createAttributeSet', 'getValue']) + ->getMock(); + + $this->attributeSetsFixtureMock = $this->getMockBuilder(AttributeSetFixture::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->attributePatternMock = $this->getMockBuilder(Pattern::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->attributePatternMock = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->attributePatternMock = $this->getMockBuilder(Pattern::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = (new ObjectManager($this))->getObject(ConfigurableProductsFixture::class, [ + 'fixtureModel' => $this->fixtureModelMock, + 'attributeSetsFixture' => $this->attributeSetsFixtureMock, + 'attributePattern' => $this->attributePatternMock, + ]); } + /** + * @SuppressWarnings(PHPMD) + */ public function testExecute() { - $importMock = $this->getMock(\Magento\ImportExport\Model\Import::class, [], [], '', false); - - $contextMock = $this->getMock(\Magento\Framework\Model\ResourceModel\Db\Context::class, [], [], '', false); - $abstractDbMock = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, - [$contextMock], - '', - true, - true, - true, - ['getAllChildren'] - ); - $abstractDbMock->expects($this->once()) - ->method('getAllChildren') - ->will($this->returnValue([1])); - - $categoryMock = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); - $categoryMock->expects($this->once()) - ->method('getResource') - ->will($this->returnValue($abstractDbMock)); - $categoryMock->expects($this->exactly(3)) - ->method('getName') - ->will($this->returnValue('category_name')); - $categoryMock->expects($this->once()) - ->method('getPath') - ->will($this->returnValue('path/to/category')); - $categoryMock->expects($this->exactly(4)) - ->method('load') - ->willReturnSelf(); - - $storeMock = $this->getMock(\Magento\Store\Model\Store::class, [], [], '', false); - $storeMock->expects($this->once()) - ->method('getRootCategoryId') - ->will($this->returnValue([2])); - - $websiteMock = $this->getMock(\Magento\Store\Model\Website::class, [], [], '', false); - $websiteMock->expects($this->once()) - ->method('getCode') - ->will($this->returnValue('website_code')); - $websiteMock->expects($this->once()) - ->method('getGroups') - ->will($this->returnValue([$storeMock])); - - $storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManager::class, [], [], '', false); - $storeManagerMock->expects($this->once()) - ->method('getWebsites') - ->will($this->returnValue([$websiteMock])); - - $source = $this->getMockBuilder(Generator::class)->disableOriginalConstructor()->getMock(); - - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - - $objectManagerMock->expects($this->at(0)) - ->method('get') - ->with(\Magento\Store\Model\StoreManager::class) - ->willReturn($storeManagerMock); + $importMock = $this->getMockBuilder(\Magento\ImportExport\Model\Import::class) + ->disableOriginalConstructor() + ->getMock(); - $objectManagerMock->expects($this->at(1)) - ->method('create') - ->will($this->returnValue($categoryMock)); + $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + ->disableOriginalConstructor() + ->getMock(); - $objectManagerMock->expects($this->at(2)) - ->method('create') - ->with(\Magento\ImportExport\Model\Import::class) - ->willReturn($importMock); + $storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $source = $this->getMockBuilder(\Magento\Setup\Model\Complex\Generator::class) + ->disableOriginalConstructor() + ->getMock(); + + $objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManager\ObjectManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $attributeSetRepositoryMock = $this->getMockForAbstractClass( + \Magento\Catalog\Api\AttributeSetRepositoryInterface::class + ); + + $productAttributeOptionManagementInterface = $this->getMockForAbstractClass( + \Magento\Catalog\Api\ProductAttributeOptionManagementInterface::class + ); - $objectManagerMock->expects($this->at(3)) + $objectManagerMock->expects($this->any()) + ->method('get') + ->will($this->returnValueMap([ + [ + \Magento\Store\Model\StoreManager::class, + $storeManagerMock + ], + [ + \Magento\Catalog\Api\AttributeSetRepositoryInterface::class, + $attributeSetRepositoryMock + ], + [ + \Magento\Catalog\Api\ProductAttributeOptionManagementInterface::class, + $productAttributeOptionManagementInterface + ] + ])); + + $attributeCollectionFactoryMock = $this->getMockBuilder(CollectionFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $objectManagerMock->expects($this->any()) ->method('create') - ->with(Generator::class) - ->willReturn($source); - $importMock->expects($this->once())->method('validateSource')->with($source)->willReturn(1); - $importMock->expects($this->once())->method('importSource')->willReturn(1); + ->will( + $this->returnCallback( + function ($className) use ( + $attributeCollectionFactoryMock, + $categoryMock, + $importMock, + $source + ) { + if ($className === CollectionFactory::class) { + return $attributeCollectionFactoryMock; + } + + if ($className === \Magento\Catalog\Model\Category::class) { + return $categoryMock; + } + + if ($className === \Magento\ImportExport\Model\Import::class) { + return $importMock; + } + + if ($className === \Magento\Setup\Model\Complex\Generator::class) { + return $source; + } + + return null; + } + ) + ); + + $valuesMap = [ + ['configurable_products', 0, 1], + ['simple_products', 0, 1], + ['search_terms', null, ['search_term' =>[['term' => 'iphone 6', 'count' => '1']]]], + ['configurable_products_variation', 3, 1], + [ + 'search_config', + null, + [ + 'max_amount_of_words_description' => '200', + 'max_amount_of_words_short_description' => '20', + 'min_amount_of_words_description' => '20', + 'min_amount_of_words_short_description' => '5' + ] + ], + ['attribute_sets', + null, + [ + 'attribute_set' => [ + [ + 'name' => 'attribute set name', + 'attributes' => [ + 'attribute' => [ + [ + 'is_required' => 1, + 'is_visible_on_front' => 1, + 'is_visible_in_advanced_search' => 1, + 'is_filterable' => 1, + 'is_filterable_in_search' => 1, + 'default_value' => 'yellow1', + 'attribute_code' => 'mycolor', + 'is_searchable' => '1', + 'frontend_label' => 'mycolor', + 'frontend_input' => 'select', + 'options' => [ + 'option' => [ + [ + 'label' => 'yellow1', + 'value' => '' + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ]; $this->fixtureModelMock ->expects($this->any()) ->method('getValue') - ->willReturnMap([ - ['configurable_products', 0, 1], - ['configurable_products_variation', 3, 1], - ]); - - $this->fixtureModelMock - ->expects($this->atLeastOnce()) - ->method('getObjectManager') - ->will($this->returnValue($objectManagerMock)); + ->will($this->returnValueMap($valuesMap)); $this->model->execute(); } public function testNoFixtureConfigValue() { - $importMock = $this->getMock(\Magento\ImportExport\Model\Import::class, [], [], '', false); + $importMock = $this->getMockBuilder(\Magento\ImportExport\Model\Import::class) + ->disableOriginalConstructor() + ->getMock(); $importMock->expects($this->never())->method('validateSource'); $importMock->expects($this->never())->method('importSource'); - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); + $objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManager\ObjectManager::class) + ->disableOriginalConstructor() + ->getMock(); $objectManagerMock->expects($this->never()) ->method('create') ->with($this->equalTo(\Magento\ImportExport\Model\Import::class)) @@ -135,10 +231,6 @@ public function testNoFixtureConfigValue() ->expects($this->never()) ->method('getObjectManager') ->will($this->returnValue($objectManagerMock)); - $this->fixtureModelMock - ->expects($this->once()) - ->method('getValue') - ->willReturn(false); $this->model->execute(); } @@ -150,8 +242,6 @@ public function testGetActionTitle() public function testIntroduceParamLabels() { - $this->assertSame([ - 'configurable_products' => 'Configurable products', - ], $this->model->introduceParamLabels()); + $this->assertSame([], $this->model->introduceParamLabels()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomerGroupsFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomerGroupsFixtureTest.php new file mode 100644 index 0000000000000..1e2c90be42606 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomerGroupsFixtureTest.php @@ -0,0 +1,123 @@ +fixtureModelMock = $this->getMockBuilder(\Magento\Setup\Fixtures\FixtureModel::class) + ->disableOriginalConstructor() + ->getMock(); + + //Mock repository for customer groups + $this->groupRepositoryMock = $this->getMockBuilder(GroupRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + //Mock for customer groups collection + $this->groupCollectionFactoryMock = $this->getMockBuilder(CollectionFactory::class) + ->setMethods(['create', 'getSize']) + ->disableOriginalConstructor() + ->getMock(); + + $this->groupCollectionFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->groupCollectionFactoryMock); + + $this->groupCollectionFactoryMock + ->expects($this->once()) + ->method('getSize') + ->willReturn(0); + + //Mock customer groups data object + $this->groupDataObjectMock = $this->getMockBuilder(GroupInterface::class) + ->setMethods(['setCode', 'setTaxClassId', 'save']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + //Mock customer groups factory + $this->groupFactoryMock = $this->getMockBuilder(GroupInterfaceFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $this->groupFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->groupDataObjectMock); + + $this->groupDataObjectMock + ->expects($this->once()) + ->method('setCode') + ->willReturn($this->groupDataObjectMock); + + $this->groupDataObjectMock + ->expects($this->once()) + ->method('setTaxClassId') + ->willReturn($this->groupDataObjectMock); + + $this->groupRepositoryMock + ->expects($this->once()) + ->method('save') + ->willReturn($this->groupDataObjectMock); + + $this->fixtureModelMock + ->expects($this->once()) + ->method('getValue') + ->will($this->returnValue(1)); + + $this->model = new CustomerGroupsFixture( + $this->fixtureModelMock, + $this->groupCollectionFactoryMock, + $this->groupRepositoryMock, + $this->groupFactoryMock + ); + + $this->model->execute(); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomersFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomersFixtureTest.php index 2ec140b4412ed..732ae4c2bbd51 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomersFixtureTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/CustomersFixtureTest.php @@ -1,11 +1,12 @@ fixtureModelMock = $this->getMock(\Magento\Setup\Fixtures\FixtureModel::class, [], [], '', false); - - $this->model = new CustomersFixture($this->fixtureModelMock); + $this->fixtureModelMock = $this->getMock( + \Magento\Setup\Fixtures\FixtureModel::class, + [], + [], + '', + false + ); + + $this->customerGeneratorMock = $this->getMock( + \Magento\Setup\Model\FixtureGenerator\CustomerGenerator::class, + [], + [], + '', + false + ); + + $this->customerDataGeneratorFactoryMock = $this->getMock( + \Magento\Setup\Model\Customer\CustomerDataGeneratorFactory::class, + [], + [], + '', + false + ); + + $this->collectionFactoryMock = $this->getMock( + \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory::class, + ['create'], + [], + '', + false + ); + + $this->collectionMock = $this->getMock( + \Magento\Customer\Model\ResourceModel\Customer\Collection::class, + [], + [], + '', + false + ); + + $this->model = (new ObjectManager($this))->getObject(CustomersFixture::class, [ + 'fixtureModel' => $this->fixtureModelMock, + 'customerGenerator' => $this->customerGeneratorMock, + 'customerDataGeneratorFactory' => $this->customerDataGeneratorFactoryMock, + 'collectionFactory' => $this->collectionFactoryMock + ]); } public function testExecute() { - $importMock = $this->getMock(\Magento\ImportExport\Model\Import::class, [], [], '', false); - - $storeMock = $this->getMock(\Magento\Store\Model\Store::class, [], [], '', false); - $storeMock->expects($this->once()) - ->method('getCode') - ->will($this->returnValue('store_code')); - - $websiteMock = $this->getMock(\Magento\Store\Model\Website::class, [], [], '', false); - $websiteMock->expects($this->once()) - ->method('getCode') - ->will($this->returnValue('website_code')); - - $storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManager::class, [], [], '', false); - $storeManagerMock->expects($this->once()) - ->method('getDefaultStoreView') - ->will($this->returnValue($storeMock)); - $storeManagerMock->expects($this->once()) - ->method('getWebsites') - ->will($this->returnValue([$websiteMock])); - - $valueMap = [ - [ - \Magento\ImportExport\Model\Import::class, - [ - 'data' => [ - 'entity' => 'customer_composite', - 'behavior' => 'append', - 'validation_strategy' => 'validation-stop-on-errors' - ] - ], - $importMock - ], - [\Magento\Store\Model\StoreManager::class, [], $storeManagerMock] + $entitiesInDB = 20; + $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($this->collectionMock); + $this->collectionMock->expects($this->once())->method('getSize')->willReturn($entitiesInDB); + + $customersNumber = 100500; + $customerConfig = [ + 'some-key' => 'some value' ]; - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->exactly(2)) + $this->fixtureModelMock + ->expects($this->exactly(2)) + ->method('getValue') + ->will($this->onConsecutiveCalls($customersNumber, $customerConfig)); + + $customerDataGeneratorMock = $this->getMock( + \Magento\Setup\Model\Customer\CustomerDataGenerator::class, + [], + [], + '', + false + ); + + $this->customerDataGeneratorFactoryMock + ->expects($this->once()) ->method('create') - ->will($this->returnValueMap($valueMap)); + ->with($customerConfig) + ->willReturn($customerDataGeneratorMock); - $this->fixtureModelMock + $this->customerGeneratorMock ->expects($this->once()) - ->method('getValue') - ->will($this->returnValue(1)); - $this->fixtureModelMock - ->expects($this->exactly(2)) - ->method('getObjectManager') - ->will($this->returnValue($objectManagerMock)); + ->method('generate') + ->with( + $customersNumber - $entitiesInDB, + $this->arrayHasKey('customer_data') + ); $this->model->execute(); } - public function testNoFixtureConfigValue() + public function testDoNoExecuteIfCustomersAlreadyGenerated() { - $importMock = $this->getMock(\Magento\ImportExport\Model\Import::class, [], [], '', false); - $importMock->expects($this->never())->method('validateSource'); - $importMock->expects($this->never())->method('importSource'); - - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->never()) - ->method('create') - ->with($this->equalTo(\Magento\ImportExport\Model\Import::class)) - ->willReturn($importMock); - - $this->fixtureModelMock - ->expects($this->never()) - ->method('getObjectManager') - ->will($this->returnValue($objectManagerMock)); + $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($this->collectionMock); + $this->collectionMock->expects($this->once())->method('getSize')->willReturn(20); $this->fixtureModelMock ->expects($this->once()) ->method('getValue') - ->willReturn(false); + ->willReturn(20); + $this->customerDataGeneratorFactoryMock->expects($this->never())->method('create'); $this->model->execute(); } @@ -112,8 +152,11 @@ public function testGetActionTitle() public function testIntroduceParamLabels() { - $this->assertSame([ - 'customers' => 'Customers' - ], $this->model->introduceParamLabels()); + $this->assertSame( + [ + 'customers' => 'Customers' + ], + $this->model->introduceParamLabels() + ); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/EavVariationsFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/EavVariationsFixtureTest.php index d7d127ddd3b72..81996008f1bb2 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/EavVariationsFixtureTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/EavVariationsFixtureTest.php @@ -1,18 +1,24 @@ fixtureModelMock = $this->getMock(\Magento\Setup\Fixtures\FixtureModel::class, [], [], '', false); - - $this->model = new EavVariationsFixture($this->fixtureModelMock); - } + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; - public function testExecute() - { - $attributeMock = $this->getMock( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, - [ - 'setAttributeSetId', - 'setAttributeGroupId', - 'save', - ], - [], - '', - false - ); - $attributeMock->expects($this->exactly(2)) - ->method('setAttributeSetId') - ->willReturnSelf(); - $attributeMock->expects($this->once()) - ->method('setAttributeGroupId') - ->willReturnSelf(); + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $attributeSetMock; - $storeMock = $this->getMock(\Magento\Store\Model\Store::class, [], [], '', false); + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $cacheMock; - $storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManager::class, [], [], '', false); - $storeManagerMock->expects($this->once()) - ->method('getStores') - ->will($this->returnValue([$storeMock])); + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $eavConfigMock; - $setMock = $this->getMock(\Magento\Eav\Model\Entity\Attribute\Set::class, [], [], '', false); - $setMock->expects($this->once()) - ->method('getDefaultGroupId') - ->will($this->returnValue(2)); + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $attributeFactoryMock; - $cacheMock = $this->getMock(\Magento\Framework\App\CacheInterface::class, [], [], '', false); + public function setUp() + { + $this->fixtureModelMock = $this->getMock(FixtureModel::class, [], [], '', false); + $this->eavConfigMock = $this->getMock(Config::class, [], [], '', false); + $this->storeManagerMock = $this->getMock(StoreManager::class, [], [], '', false); + $this->attributeSetMock = $this->getMock(Set::class, [], [], '', false); + $this->cacheMock = $this->getMock(CacheInterface::class, [], [], '', false); + $this->attributeFactoryMock = $this->getMock(AttributeFactory::class, ['create'], [], '', false); + + $this->model = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( + EavVariationsFixture::class, + [ + 'fixtureModel' => $this->fixtureModelMock, + 'eavConfig' => $this->eavConfigMock, + 'storeManager' => $this->storeManagerMock, + 'attributeSet' => $this->attributeSetMock, + 'cache' => $this->cacheMock, + 'attributeFactory' => $this->attributeFactoryMock, + ] + ); + } - $valueMap = [ - [\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, [], $attributeMock], - [\Magento\Store\Model\StoreManager::class, [], $storeManagerMock], - [\Magento\Eav\Model\Entity\Attribute\Set::class, $setMock], - [\Magento\Framework\App\CacheInterface::class, $cacheMock], - ]; + public function testDoNotExecuteWhenAttributeAleadyExist() + { + $this->fixtureModelMock + ->expects($this->any()) + ->method('getValue') + ->with('configurable_products', []) + ->willReturn(10); + $this->eavConfigMock->expects($this->once())->method('getEntityAttributeCodes') + ->willReturn(['configurable_variation']); + $this->attributeFactoryMock->expects($this->never())->method('create'); - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->exactly(2)) - ->method('create') - ->will($this->returnValueMap($valueMap)); - $objectManagerMock->expects($this->exactly(2)) - ->method('get') - ->will($this->returnValueMap($valueMap)); + $this->model->execute(); + } + public function testExecute() + { + $this->eavConfigMock->expects($this->once())->method('getEntityAttributeCodes') + ->willReturn(['attr1', 'attr2']); $this->fixtureModelMock ->expects($this->any()) ->method('getValue') ->willReturnMap([ - ['configurable_products', 0, 1], + ['configurable_products', [], ['some-config']], ['configurable_products_variation', 3, 1], ]); - $this->fixtureModelMock - ->expects($this->exactly(4)) - ->method('getObjectManager') - ->will($this->returnValue($objectManagerMock)); - - $this->model->execute(); - } - - public function testNoFixtureConfigValue() - { - $attributeMock = $this->getMock(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, [], [], '', false); - $attributeMock->expects($this->never())->method('save'); + $storeMock = $this->getMock(\Magento\Store\Model\Store::class, [], [], '', false); + $this->storeManagerMock->expects($this->once()) + ->method('getStores') + ->will($this->returnValue([$storeMock])); - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->never()) - ->method('create') - ->with($this->equalTo(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)) - ->willReturn($attributeMock); + $this->attributeSetMock->expects($this->once())->method('load')->willReturnSelf(); + $this->attributeSetMock->expects($this->once()) + ->method('getDefaultGroupId') + ->will($this->returnValue(2)); - $this->fixtureModelMock - ->expects($this->never()) - ->method('getObjectManager') - ->will($this->returnValue($objectManagerMock)); - $this->fixtureModelMock - ->expects($this->once()) - ->method('getValue') - ->willReturn(false); + $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->setMethods([ + 'setAttributeSetId', + 'setAttributeGroupId', + 'save', + ])->disableOriginalConstructor() + ->getMock(); + $attributeMock->expects($this->exactly(2)) + ->method('setAttributeSetId') + ->willReturnSelf(); + $attributeMock->expects($this->once()) + ->method('setAttributeGroupId') + ->willReturnSelf(); + $this->attributeFactoryMock->expects($this->once())->method('create') + ->with( + [ + 'data' => [ + 'frontend_label' => [ + 'configurable variations', + 'configurable variations', + ], + 'frontend_input' => 'select', + 'is_required' => '0', + 'option' => [ + 'order' => ['option_1' => 1], + 'value' => ['option_1' => ['option 1', 'option 1']], + 'delete' => ['option_1' => ''], + ], + 'default' => ['option_0'], + 'attribute_code' => 'configurable_variation', + 'is_global' => '1', + 'default_value_text' => '', + 'default_value_yesno' => '0', + 'default_value_date' => '', + 'default_value_textarea' => '', + 'is_unique' => '0', + 'is_searchable' => '1', + 'is_visible_in_advanced_search' => '0', + 'is_comparable' => '0', + 'is_filterable' => '1', + 'is_filterable_in_search' => '0', + 'is_used_for_promo_rules' => '0', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '0', + 'used_in_product_listing' => '0', + 'used_for_sort_by' => '0', + 'source_model' => null, + 'backend_model' => null, + 'apply_to' => [], + 'backend_type' => 'int', + 'entity_type_id' => 4, + 'is_user_defined' => 1, + 'swatch_input_type' => 'visual', + 'swatchvisual' => [ + 'value' => ['option_1' => '#ffffff'], + ], + 'optionvisual' => [ + 'value' => ['option_1' => ['option 1']], + ], + ] + ] + )->willReturn($attributeMock); + $this->cacheMock->expects($this->once())->method('remove')->with(Config::ATTRIBUTES_CACHE_ID . Product::ENTITY); $this->model->execute(); } public function testGetActionTitle() { - $eavVariationsFixture = new EavVariationsFixture($this->fixtureModelMock); - $this->assertSame('Generating configurable EAV variations', $eavVariationsFixture->getActionTitle()); + $this->assertSame('Generating configurable EAV variations', $this->model->getActionTitle()); } public function testIntroduceParamLabels() { - $eavVariationsFixture = new EavVariationsFixture($this->fixtureModelMock); - $this->assertSame([], $eavVariationsFixture->introduceParamLabels()); + $this->assertSame([], $this->model->introduceParamLabels()); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/FixtureConfigTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/FixtureConfigTest.php new file mode 100644 index 0000000000000..134773e439eb8 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/FixtureConfigTest.php @@ -0,0 +1,80 @@ +fileParserMock = $this->getMock(Parser::class, ['getDom', 'xmlToArray'], [], '', false); + + $this->model = new FixtureConfig($this->fileParserMock); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Profile configuration file `exception.file` is not readable or does not exists. + */ + public function testLoadConfigException() + { + $this->model->loadConfig('exception.file'); + } + + public function testLoadConfig() + { + $this->fileParserMock->expects($this->exactly(2))->method('xmlToArray')->willReturn( + ['config' => [ 'profile' => ['some_key' => 'some_value']]] + ); + + $domMock = $this->getMock(\DOMDocument::class, ['load', 'xinclude'], [], '', false); + $domMock->expects($this->once())->method('load')->with('config.file')->willReturn( + $this->fileParserMock->xmlToArray() + ); + $domMock->expects($this->once())->method('xinclude'); + $this->fileParserMock->expects($this->exactly(2))->method('getDom')->willReturn($domMock); + + $this->model->loadConfig('config.file'); + $this->assertSame('some_value', $this->model->getValue('some_key')); + } + + public function testGetValue() + { + $this->assertSame(null, $this->model->getValue('null_key')); + } +} + +namespace Magento\Setup\Fixtures; + +/** + * Overriding the built-in PHP function since it cannot be mocked-> + * + * The method is used in FixtureModel. loadConfig in an if statement. By overriding this method we are able to test + * both of the possible cases based on the return value of is_readable. + * + * @param string $filename + * @return bool + */ +function is_readable($filename) +{ + if (strpos($filename, 'exception') !== false) { + return false; + } + return true; +} diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/FixtureModelTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/FixtureModelTest.php index ee1369bd15d5a..76f22d23c8186 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/FixtureModelTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/FixtureModelTest.php @@ -1,6 +1,6 @@ getMock(\Magento\Framework\Xml\Parser::class, [], [], '', false); - - $this->model = new FixtureModel($reindexCommandMock, $fileParserMock); + $this->model = new FixtureModel($reindexCommandMock); } public function testReindex() @@ -35,57 +32,4 @@ public function testReindex() $outputMock = $this->getMock(\Symfony\Component\Console\Output\OutputInterface::class, [], [], '', false); $this->model->reindex($outputMock); } - - /** - * @expectedException \Exception - * @expectedExceptionMessage Profile configuration file `exception.file` is not readable or does not exists. - */ - public function testLoadConfigException() - { - $this->model->loadConfig('exception.file'); - } - - public function testLoadConfig() - { - $reindexCommandMock = $this->getMock( - \Magento\Indexer\Console\Command\IndexerReindexCommand::class, - [], - [], - '', - false - ); - - $fileParserMock = $this->getMock(\Magento\Framework\Xml\Parser::class, ['load', 'xmlToArray'], [], '', false); - $fileParserMock->expects($this->once())->method('xmlToArray')->willReturn( - ['config' => [ 'profile' => ['some_key' => 'some_value']]] - ); - $fileParserMock->expects($this->once())->method('load')->with('config.file')->willReturn($fileParserMock); - $this->model = new FixtureModel($reindexCommandMock, $fileParserMock); - $this->model->loadConfig('config.file'); - $this->assertSame('some_value', $this->model->getValue('some_key')); - } - - public function testGetValue() - { - $this->assertSame(null, $this->model->getValue('null_key')); - } -} - -namespace Magento\Setup\Fixtures; - -/** - * Overriding the built-in PHP function since it cannot be mocked-> - * - * The method is used in FixtureModel. loadConfig in an if statement. By overriding this method we are able to test - * both of the possible cases based on the return value of is_readable. - * - * @param string $filename - * @return bool - */ -function is_readable($filename) -{ - if (strpos($filename, 'exception') !== false) { - return false; - } - return true; } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/IndexersStatesApplyFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/IndexersStatesApplyFixtureTest.php index 56fa29d6d4b8c..12c4f00ef0e85 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/IndexersStatesApplyFixtureTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/IndexersStatesApplyFixtureTest.php @@ -1,6 +1,6 @@ fixtureModelMock = $this->getMock(\Magento\Setup\Fixtures\FixtureModel::class, [], [], '', false); - - $this->model = new OrdersFixture($this->fixtureModelMock); - } - - /** - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ public function testExecute() { - $mockObjectNames = [ - \Magento\Quote\Model\ResourceModel\Quote::class, - \Magento\Quote\Model\ResourceModel\Quote\Address::class, - \Magento\Quote\Model\ResourceModel\Quote\Item::class, - \Magento\Quote\Model\ResourceModel\Quote\Item\Option::class, - \Magento\Quote\Model\ResourceModel\Quote\Payment::class, - \Magento\Quote\Model\ResourceModel\Quote\Address\Rate::class, - \Magento\Reports\Model\ResourceModel\Event::class, - \Magento\Sales\Model\ResourceModel\Order::class, - \Magento\Sales\Model\ResourceModel\Order\Grid::class, - \Magento\Sales\Model\ResourceModel\Order\Item::class, - \Magento\Sales\Model\ResourceModel\Order\Payment::class, - \Magento\Sales\Model\ResourceModel\Order\Status\History::class, - \Magento\Eav\Model\ResourceModel\Entity\Store::class - ]; - $mockObjects = []; - - foreach ($mockObjectNames as $mockObjectName) { - $mockObject = $this->getMock($mockObjectName, ['getTable'], [], '', false); - $path = explode('\\', $mockObjectName); - $name = array_pop($path); - if (strcasecmp($mockObjectName, \Magento\Sales\Model\ResourceModel\Order::class) == 0) { - $mockObject->expects($this->exactly(2)) - ->method('getTable') - ->willReturn(strtolower($name) . '_table_name'); - } else { - $mockObject->expects($this->once()) - ->method('getTable') - ->willReturn(strtolower($name) . '_table_name'); - } - $mockObjects[] = [$mockObjectName, $mockObject]; - } - - $connectionInterfaceMock = $this->getMockForAbstractClass( - \Magento\Framework\DB\Adapter\AdapterInterface::class, - [], - '', - true, - true, - true, - [] + $storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $productCollectionFactoryMock = $this->getMockBuilder( + CollectionFactory::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $productRepositoryMock = $this->getMockBuilder(ProductRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $optionRepositoryMock = $this->getMockBuilder(OptionRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $linkManagementMock = $this->getMockBuilder(LinkManagementInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $serializerMock = $this->getMockBuilder(SerializerInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->fixtureModelMock = $this->getMockBuilder(FixtureModel::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new OrdersFixture( + $storeManagerMock, + $productCollectionFactoryMock, + $productRepositoryMock, + $optionRepositoryMock, + $linkManagementMock, + $serializerMock, + $this->fixtureModelMock ); - $connectionInterfaceMock->expects($this->exactly(14)) - ->method('getTableName') - ->willReturn('table_name'); - $resourceMock = $this->getMock(\Magento\Framework\App\ResourceConnection::class, [], [], '', false); - $resourceMock->expects($this->exactly(15)) - ->method('getConnection') - ->willReturn($connectionInterfaceMock); - - $websiteMock = $this->getMock(\Magento\Store\Model\Website::class, ['getId', 'getName'], [], '', false); - $websiteMock->expects($this->once()) - ->method('getId') - ->willReturn('website_id'); - $websiteMock->expects($this->once()) - ->method('getName') - ->willReturn('website_name'); - - $groupMock = $this->getMock(\Magento\Store\Model\Group::class, ['getName'], [], '', false); - $groupMock->expects($this->once()) - ->method('getName') - ->willReturn('group_name'); - - $storeMock = $this->getMock( - \Magento\Store\Model\Store::class, - [ - 'getStoreId', - 'getWebsite', - 'getGroup', - 'getName', - 'getRootCategoryId' - ], + $orderMock = $this->getMock( + Order::class, + ['getTable', 'getConnection', 'getTableName', 'query', 'fetchColumn'], [], '', false ); - $storeMock->expects($this->once()) - ->method('getStoreId') - ->willReturn(1); - $storeMock->expects($this->exactly(2)) - ->method('getWebsite') - ->willReturn($websiteMock); - $storeMock->expects($this->once()) - ->method('getGroup') - ->willReturn($groupMock); - $storeMock->expects($this->once()) - ->method('getName') - ->willReturn('store_name'); - $storeMock->expects($this->once()) - ->method('getRootCategoryId') - ->willReturn(1); - - $storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManager::class, [], [], '', false); - $storeManagerMock->expects($this->once()) - ->method('getStores') - ->willReturn([$storeMock]); - - $contextMock = $this->getMock(\Magento\Framework\Model\ResourceModel\Db\Context::class, [], [], '', false); - $abstractDbMock = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, - [$contextMock], - '', - true, - true, - true, - ['getAllChildren'] - ); - $abstractDbMock->expects($this->once()) - ->method('getAllChildren') - ->will($this->returnValue([1])); - - $categoryMock = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); - $categoryMock->expects($this->once()) - ->method('getResource') - ->willReturn($abstractDbMock); - $categoryMock->expects($this->once()) - ->method('getPath') - ->willReturn('path/to/category'); - $categoryMock->expects($this->exactly(2)) - ->method('getName') - ->willReturn('category_name'); - $categoryMock->expects($this->exactly(5)) - ->method('load') - ->willReturnSelf(); - - $productMock = - $this->getMock(\Magento\Catalog\Model\Product::class, ['load', 'getSku', 'getName'], [], '', false); - $productMock->expects($this->exactly(2)) - ->method('load') - ->willReturnSelf(); - $productMock->expects($this->exactly(2)) - ->method('getSku') - ->willReturn('product_sku'); - $productMock->expects($this->exactly(2)) - ->method('getName') - ->willReturn('product_name'); - - $selectMock = $this->getMock(\Magento\Framework\DB\Select::class, [], [], '', false); - - $collectionMock = - $this->getMock(\Magento\Catalog\Model\ResourceModel\Product\Collection::class, [], [], '', false); - $collectionMock->expects($this->once()) - ->method('getSelect') - ->willReturn($selectMock); - $collectionMock->expects($this->once()) - ->method('getAllIds') - ->willReturn([1, 1]); - - array_push( - $mockObjects, - [\Magento\Store\Model\StoreManager::class, [], $storeManagerMock], - [\Magento\Catalog\Model\Category::class, $categoryMock], - [\Magento\Catalog\Model\Product::class, $productMock], - [\Magento\Framework\App\ResourceConnection::class, $resourceMock], - [\Magento\Catalog\Model\ResourceModel\Product\Collection::class, [], $collectionMock] - ); - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->exactly(32)) - ->method('get') - ->will($this->returnValueMap($mockObjects)); - $objectManagerMock->expects($this->exactly(2)) - ->method('create') - ->will($this->returnValueMap($mockObjects)); + $path = explode('\\', Order::class); + $name = array_pop($path); - $this->fixtureModelMock - ->expects($this->once()) - ->method('getValue') - ->willReturn(1); - $this->fixtureModelMock - ->expects($this->exactly(34)) - ->method('getObjectManager') - ->willReturn($objectManagerMock); - - $this->model->execute(); - } - - public function testNoFixtureConfigValue() - { - $connectionMock = $this->getMockForAbstractClass( - \Magento\Framework\DB\Adapter\AdapterInterface::class, - [], - '', - true, - true, - true, - [] - ); - $connectionMock->expects($this->never()) - ->method('query'); - - $resourceMock = $this->getMock(\Magento\Framework\App\ResourceConnection::class, [], [], '', false); - $resourceMock->expects($this->never()) + $orderMock->expects($this->atLeastOnce()) ->method('getConnection') - ->with($this->equalTo('write')) - ->willReturn($connectionMock); + ->willReturn($orderMock); + $orderMock->expects($this->once()) + ->method('getTable') + ->willReturn(strtolower($name) . '_table_name'); + $orderMock->expects($this->once()) + ->method('query') + ->willReturn($orderMock); + $orderMock->expects($this->once()) + ->method('getTableName') + ->willReturn(strtolower($name) . '_table_name'); - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->never()) + $objectManagerMock = $this->getMock(ObjectManager::class, [], [], '', false); + $objectManagerMock->expects($this->atLeastOnce()) ->method('get') - ->with($this->equalTo(\Magento\Framework\App\ResourceConnection::class)) - ->willReturn($resourceMock); + ->willReturn($orderMock); $this->fixtureModelMock - ->expects($this->never()) - ->method('getObjectManagerMock') + ->expects($this->atLeastOnce()) + ->method('getObjectManager') ->willReturn($objectManagerMock); - $this->fixtureModelMock - ->expects($this->once()) - ->method('getValue') - ->willReturn(false); $this->model->execute(); } - - public function testGetActionTitle() - { - $this->assertSame('Generating orders', $this->model->getActionTitle()); - } - - public function testIntroduceParamLabels() - { - $this->assertSame([ - 'orders' => 'Orders' - ], $this->model->introduceParamLabels()); - } } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/SimpleProductsFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/SimpleProductsFixtureTest.php deleted file mode 100644 index 913b2a1e3f020..0000000000000 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/SimpleProductsFixtureTest.php +++ /dev/null @@ -1,150 +0,0 @@ -fixtureModelMock = $this->getMock(\Magento\Setup\Fixtures\FixtureModel::class, [], [], '', false); - - $this->model = new SimpleProductsFixture($this->fixtureModelMock); - } - - public function testExecute() - { - $storeMock = $this->getMock(\Magento\Store\Model\Store::class, [], [], '', false); - $storeMock->expects($this->once()) - ->method('getRootCategoryId') - ->willReturn(1); - - $websiteMock = $this->getMock(\Magento\Store\Model\Website::class, [], [], '', false); - $websiteMock->expects($this->once()) - ->method('getCode') - ->willReturn('website_code'); - $websiteMock->expects($this->once()) - ->method('getGroups') - ->willReturn([$storeMock]); - - $storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManager::class, [], [], '', false); - $storeManagerMock->expects($this->once()) - ->method('getWebsites') - ->willReturn([$websiteMock]); - - $importMock = $this->getMock(\Magento\ImportExport\Model\Import::class, [], [], '', false); - - $contextMock = $this->getMock(\Magento\Framework\Model\ResourceModel\Db\Context::class, [], [], '', false); - $abstractDbMock = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, - [$contextMock], - '', - true, - true, - true, - ['getAllChildren'] - ); - $abstractDbMock->expects($this->once()) - ->method('getAllChildren') - ->will($this->returnValue([1])); - - $categoryMock = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); - $categoryMock->expects($this->once()) - ->method('getResource') - ->willReturn($abstractDbMock); - $categoryMock->expects($this->once()) - ->method('getPath') - ->willReturn('path/to/category'); - $categoryMock->expects($this->exactly(5)) - ->method('load') - ->willReturnSelf(); - $categoryMock->expects($this->exactly(3)) - ->method('getName') - ->willReturn('category_name'); - - $valueMap = [ - [ - \Magento\ImportExport\Model\Import::class, - [ - 'data' => [ - 'entity' => 'catalog_product', - 'behavior' => 'append', - 'validation_strategy' => 'validation-stop-on-errors' - ] - ], - $importMock - ], - [\Magento\Store\Model\StoreManager::class, [], $storeManagerMock] - ]; - - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->exactly(2)) - ->method('create') - ->will($this->returnValueMap($valueMap)); - $objectManagerMock->expects($this->once()) - ->method('get') - ->willReturn($categoryMock); - - $this->fixtureModelMock - ->expects($this->once()) - ->method('getValue') - ->willReturn(1); - $this->fixtureModelMock - ->expects($this->exactly(3)) - ->method('getObjectManager') - ->willReturn($objectManagerMock); - - $this->model->execute(); - } - - public function testNoFixtureConfigValue() - { - $importMock = $this->getMock(\Magento\ImportExport\Model\Import::class, [], [], '', false); - $importMock->expects($this->never())->method('validateSource'); - $importMock->expects($this->never())->method('importSource'); - - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->never()) - ->method('create') - ->with($this->equalTo(\Magento\ImportExport\Model\Import::class)) - ->willReturn($importMock); - - $this->fixtureModelMock - ->expects($this->never()) - ->method('getObjectManager') - ->will($this->returnValue($objectManagerMock)); - $this->fixtureModelMock - ->expects($this->once()) - ->method('getValue') - ->willReturn(false); - - $this->model->execute(); - } - - public function testGetActionTitle() - { - $this->assertSame('Generating simple products', $this->model->getActionTitle()); - } - - public function testIntroduceParamLabels() - { - $this->assertSame([ - 'simple_products' => 'Simple products' - ], $this->model->introduceParamLabels()); - } -} diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/StoresFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/StoresFixtureTest.php index 7c1c37cc81a5e..dbd22962a23f1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/StoresFixtureTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/StoresFixtureTest.php @@ -1,18 +1,28 @@ fixtureModelMock = $this->getMock(\Magento\Setup\Fixtures\FixtureModel::class, [], [], '', false); + /** + * @var StoreManager + */ + private $storeManagerMock; - $this->model = new StoresFixture($this->fixtureModelMock); - } + /** + * @var ManagerInterface + */ + private $eventManagerMock; /** - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @var CategoryFactory */ - public function testExecute() - { - $websiteMock = $this->getMock(\Magento\Store\Model\Website::class, [], [], '', false); - $websiteMock->expects($this->exactly(2)) - ->method('getId') - ->willReturn('website_id'); - $websiteMock->expects($this->once()) - ->method('save'); + private $categoryFactoryMock; - $groupMock = $this->getMock(\Magento\Store\Model\Group::class, [], [], '', false); - $groupMock->expects($this->exactly(2)) - ->method('getId') - ->willReturn('group_id'); - $groupMock->expects($this->once()) - ->method('save'); + /** + * @var Writer + */ + private $scopeConfigMock; - $storeMock = $this->getMock(\Magento\Store\Model\Store::class, [], [], '', false); - $storeMock->expects($this->once()) - ->method('getRootCategoryId') - ->willReturn(1); - $storeMock->expects($this->once()) - ->method('getId') - ->willReturn('store_id'); - $storeMock->expects($this->once()) - ->method('save'); + /** + * @var Config + */ + private $localeConfigMock; - $storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManager::class, [], [], '', false); - $storeManagerMock->expects($this->once()) - ->method('getWebsite') - ->willReturn($websiteMock); - $storeManagerMock->expects($this->once()) - ->method('getGroup') - ->willReturn($groupMock); - $storeManagerMock->expects($this->once()) - ->method('getDefaultStoreView') - ->willReturn($storeMock); - $storeManagerMock->expects($this->once()) - ->method('getStore') - ->willReturn($storeMock); + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testExecute() + { + $this->fixtureModelMock = $this->getMockBuilder(FixtureModel::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->storeManagerMock = $this->getMockBuilder(StoreManager::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'getGroup', + 'getGroups', + 'getWebsite', + 'getDefaultStoreView', + 'getStore', + 'getStores', + 'reinitStores' + ] + )->getMock(); + + $this->eventManagerMock = $this->getMockBuilder(ManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->categoryFactoryMock = $this->getMockBuilder(CategoryFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $categoryMock = $this->getMockBuilder(CategoryInterface::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'create', + 'setName', + 'setPath', + 'setLevel', + 'setAvailableSortBy', + 'setDefaultSortBy', + 'setIsActive', + 'save' + ] + ) + ->getMockForAbstractClass(); + + $this->categoryFactoryMock->expects($this->exactly(5)) + ->method('create') + ->willReturn($categoryMock); - $categoryMock = $this->getMock( - \Magento\Catalog\Model\Category::class, - [ - 'setName', - 'setPath', - 'setLevel', - 'setAvailableSortBy', - 'setDefaultSortBy', - 'setIsActive', - 'getId', - 'save', - 'load' - ], - [], - '', - false - ); - $categoryMock->expects($this->once()) + $categoryMock->expects($this->exactly(5)) ->method('setName') - ->willReturnSelf(); - $categoryMock->expects($this->once()) + ->willReturn($categoryMock); + + $categoryMock->expects($this->exactly(5)) ->method('setPath') - ->willReturnSelf(); - $categoryMock->expects($this->once()) + ->willReturn($categoryMock); + + $categoryMock->expects($this->exactly(5)) ->method('setLevel') - ->willReturnSelf(); - $categoryMock->expects($this->once()) + ->willReturn($categoryMock); + + $categoryMock->expects($this->exactly(5)) ->method('setAvailableSortBy') - ->willReturnSelf(); - $categoryMock->expects($this->once()) + ->willReturn($categoryMock); + + $categoryMock->expects($this->exactly(5)) ->method('setDefaultSortBy') - ->willReturnSelf(); - $categoryMock->expects($this->once()) + ->willReturn($categoryMock); + + $categoryMock->expects($this->exactly(5)) ->method('setIsActive') - ->willReturnSelf(); - $categoryMock->expects($this->once()) + ->willReturn($categoryMock); + + $categoryMock->expects($this->exactly(5)) ->method('getId') - ->willReturn('category_id'); + ->willReturn($categoryMock); - $valueMap = [ - [\Magento\Store\Model\StoreManager::class, [], $storeManagerMock], - [\Magento\Catalog\Model\Category::class, [], $categoryMock] - ]; + $categoryMock->expects($this->exactly(5)) + ->method('save') + ->willReturn($categoryMock); - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->exactly(2)) - ->method('create') - ->will($this->returnValueMap($valueMap)); + $this->localeConfigMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->setMethods(['getAllowedLocales']) + ->getMock(); - $this->fixtureModelMock - ->expects($this->exactly(3)) - ->method('getValue') - ->will($this->returnValue(1)); - $this->fixtureModelMock - ->expects($this->exactly(2)) - ->method('getObjectManager') - ->willReturn($objectManagerMock); + $this->localeConfigMock->expects($this->once()) + ->method('getAllowedLocales') + ->willReturn(['en_US']); - $this->model->execute(); - } + $this->scopeConfigMock = $this->getMockBuilder(Writer::class) + ->disableOriginalConstructor() + ->getMock(); - public function testNoFixtureConfigValue() - { - $storeMock = $this->getMock(\Magento\Store\Model\Store::class, [], [], '', false); - $storeMock->expects($this->never())->method('save'); + $storeMock = $this->getMockBuilder(StoreInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getId', 'getRootCategoryId', 'addData', 'save']) + ->getMockForAbstractClass(); - $storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManager::class, [], [], '', false); - $storeManagerMock->expects($this->never()) - ->method('getDefaultStoreView') + $storeMock->expects($this->exactly(11)) + ->method('getId') + ->willReturn(1); + + $storeMock->expects($this->exactly(11)) + ->method('addData') ->willReturn($storeMock); - $objectManagerMock = $this->getMock(\Magento\Framework\ObjectManager\ObjectManager::class, [], [], '', false); - $objectManagerMock->expects($this->never()) - ->method('create') - ->with($this->equalTo(\Magento\Store\Model\StoreManager::class)) - ->willReturn($storeManagerMock); + $storeGroupMock = $this->getMockBuilder(GroupInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getId', 'addData', 'save']) + ->getMockForAbstractClass(); + + $storeGroupMock->expects($this->exactly(11)) + ->method('getId') + ->willReturn(1); + + $storeGroupMock->expects($this->exactly(5)) + ->method('addData') + ->willReturn($storeGroupMock); + + $websiteMock = $this->getMockBuilder(WebsiteInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getId', 'addData', 'save']) + ->getMockForAbstractClass(); + + $websiteMock->expects($this->exactly(3)) + ->method('getId') + ->willReturn(1); + + $websiteMock->expects($this->exactly(2)) + ->method('addData') + ->willReturn($storeGroupMock); + + $this->storeManagerMock->expects($this->once()) + ->method('reinitStores') + ->willReturn('void'); + + $this->storeManagerMock->expects($this->once()) + ->method('getGroups') + ->willReturn([$storeGroupMock]); + + $this->storeManagerMock->expects($this->once()) + ->method('getGroup') + ->willReturn($storeGroupMock); + + $this->storeManagerMock->expects($this->once()) + ->method('getWebsite') + ->willReturn($websiteMock); + + $this->storeManagerMock->expects($this->once()) + ->method('getStores') + ->willReturn([$storeMock]); + + $this->storeManagerMock->expects($this->once()) + ->method('getDefaultStoreView') + ->willReturn($storeMock); $this->fixtureModelMock - ->expects($this->never()) - ->method('getObjectManager') - ->willReturn($objectManagerMock); - $this->fixtureModelMock - ->expects($this->exactly(3)) + ->expects($this->exactly(4)) ->method('getValue') - ->willReturn(false); + ->will($this->returnValueMap( + [ + ['websites', 1, 3], + ['store_groups', 1, 6], + ['store_views', 1, 12], + ['assign_entities_to_all_websites', false] + ] + )); + + $this->model = new StoresFixture( + $this->fixtureModelMock, + $this->storeManagerMock, + $this->eventManagerMock, + $this->categoryFactoryMock, + $this->localeConfigMock, + $this->scopeConfigMock + ); $this->model->execute(); } - - public function testGetActionTitle() - { - $this->assertSame('Generating websites, stores and store views', $this->model->getActionTitle()); - } - - public function testIntroduceParamLabels() - { - $this->assertSame([ - 'websites' => 'Websites', - 'store_groups' => 'Store Groups', - 'store_views' => 'Store Views' - ], $this->model->introduceParamLabels()); - } } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/TaxRatesFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/TaxRatesFixtureTest.php index f7a4cc777b334..9e4ef50e33151 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/TaxRatesFixtureTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/TaxRatesFixtureTest.php @@ -1,6 +1,6 @@ fixtureModelMock = $this->getMockBuilder(\Magento\Setup\Fixtures\FixtureModel::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->taxRateFactoryMock = $this->getMockBuilder(TaxRateInterfaceFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->taxRateRepositoryMock = $this->getMockBuilder(TaxRateRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->configWriterMock = $this->getMockBuilder(ConfigWriter::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->taxRuleFactoryMock = $this->getMockBuilder(TaxRuleInterfaceFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->taxRuleRepositoryMock = $this->getMockBuilder(TaxRuleRepositoryInterface::class) + ->disableOriginalConstructor() + ->setMethods(['save', 'get', 'delete', 'deleteById', 'getList']) + ->getMock(); + + $this->fixtureModelMock + ->expects($this->exactly(2)) + ->method('getValue') + ->will($this->returnValueMap( + [ + ['tax_mode', 'VAT'], + ['tax_rules', 2] + ] + )); + + $this->taxRateCollectionFactoryMock = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $taxRateCollectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->setMethods(['getAllIds']) + ->getMock(); + + $this->taxRateCollectionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($taxRateCollectionMock); + + $taxRateCollectionMock->expects($this->once()) + ->method('getAllIds') + ->willReturn([1]); + + $this->model = new TaxRulesFixture( + $this->fixtureModelMock, + $this->taxRuleRepositoryMock, + $this->taxRuleFactoryMock, + $this->taxRateCollectionFactoryMock, + $this->taxRateFactoryMock, + $this->taxRateRepositoryMock, + $this->configWriterMock + ); + + $this->model->execute(); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Address/AddressDataGeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Address/AddressDataGeneratorTest.php new file mode 100644 index 0000000000000..68825ae774452 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Address/AddressDataGeneratorTest.php @@ -0,0 +1,46 @@ +addressGenerator = new \Magento\Setup\Model\Address\AddressDataGenerator(); + } + + public function testPostcode() + { + mt_srand(42); + $address1 = $this->addressGenerator->generateAddress(); + + mt_srand(66); + $address2 = $this->addressGenerator->generateAddress(); + + $this->assertNotEquals($address1['postcode'], $address2['postcode']); + } + + public function testAddressStructure() + { + $address = $this->addressGenerator->generateAddress(); + + foreach ($this->addressStructure as $addressField) { + $this->assertTrue(array_key_exists($addressField, $address)); + } + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php index 79f393ff6cc1a..f1ea8d843f8ee 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/AdminAccountFactoryTest.php @@ -1,6 +1,6 @@ '%s', 'name' => 'Static', - 'calculated' => function ($index) { - return $index * 10; + 'calculated' => function ($index, $generatedKey) { + return $index * 10 + $generatedKey; }, ], [ @@ -53,7 +53,7 @@ public function patternDataProvider() 'name' => 'yyy %s' ], ], - 'ecpectedCount' => 3, + 'expectedCount' => 3, 'expectedRowsResult' => [ ['id' => '1', 'name' => 'Static', 'calculated' => 10], ['id' => '', 'name' => 'xxx 1', 'calculated' => ''], @@ -68,7 +68,7 @@ public function patternDataProvider() 'calculated' => 'calc %s', ], ], - 'ecpectedCount' => 1, + 'expectedCount' => 1, 'expectedRowsResult' => [ ['id' => '1', 'name' => 'Dynamic 1', 'calculated' => 'calc 1'], ], diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php index 033ab78c69cd0..6c5b7ce339613 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php @@ -1,6 +1,6 @@ assertSame('Encryption key', $options[0]->getDescription()); $this->assertInstanceOf(\Magento\Framework\Setup\Option\SelectConfigOption::class, $options[1]); $this->assertSame('Session save handler', $options[1]->getDescription()); - $this->assertInstanceOf(\Magento\Framework\Setup\Option\SelectConfigOption::class, $options[2]); - $this->assertSame('Type of definitions used by Object Manager', $options[2]->getDescription()); + $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[2]); + $this->assertSame('Database server host', $options[2]->getDescription()); $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[3]); - $this->assertSame('Database server host', $options[3]->getDescription()); + $this->assertSame('Database name', $options[3]->getDescription()); $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[4]); - $this->assertSame('Database name', $options[4]->getDescription()); + $this->assertSame('Database server username', $options[4]->getDescription()); $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[5]); - $this->assertSame('Database server username', $options[5]->getDescription()); + $this->assertSame('Database server engine', $options[5]->getDescription()); $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[6]); - $this->assertSame('Database server engine', $options[6]->getDescription()); + $this->assertSame('Database server password', $options[6]->getDescription()); $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[7]); - $this->assertSame('Database server password', $options[7]->getDescription()); + $this->assertSame('Database table prefix', $options[7]->getDescription()); $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[8]); - $this->assertSame('Database table prefix', $options[8]->getDescription()); + $this->assertSame('Database type', $options[8]->getDescription()); $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[9]); - $this->assertSame('Database type', $options[9]->getDescription()); - $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[10]); - $this->assertSame('Database initial set of commands', $options[10]->getDescription()); - $this->assertInstanceOf(\Magento\Framework\Setup\Option\FlagConfigOption::class, $options[11]); + $this->assertSame('Database initial set of commands', $options[9]->getDescription()); + $this->assertInstanceOf(\Magento\Framework\Setup\Option\FlagConfigOption::class, $options[10]); $this->assertSame( 'If specified, then db connection validation will be skipped', - $options[11]->getDescription() + $options[10]->getDescription() ); - $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[12]); - $this->assertSame('http Cache hosts', $options[12]->getDescription()); - $this->assertEquals(13, count($options)); + $this->assertInstanceOf(\Magento\Framework\Setup\Option\TextConfigOption::class, $options[11]); + $this->assertSame('http Cache hosts', $options[11]->getDescription()); + $this->assertEquals(12, count($options)); } public function testCreateOptions() diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Cron/Helper/ModuleUninstallTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Cron/Helper/ModuleUninstallTest.php index 4fe96a37c724a..143e0b4e8c1da 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/Cron/Helper/ModuleUninstallTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/Cron/Helper/ModuleUninstallTest.php @@ -1,6 +1,6 @@ getMock(\Magento\Setup\Model\ObjectManagerProvider::class, [], [], '', false); $objectManager = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class, [], '', false); @@ -62,18 +66,16 @@ public function testSetCache($commandClass, $arrayInput, $jobName, $params) */ public function setCacheDataProvider() { - $cacheEnable = new ArrayInput(['command' => 'cache:enable', 'types' => ['cache1']]); - $cacheDisable = new ArrayInput(['command' => 'cache:disable']); return [ [ \Magento\Backend\Console\Command\CacheEnableCommand::class, - $cacheEnable, + ['command' => 'cache:enable', 'types' => ['cache1']], 'setup:cache:enable', ['cache1'] ], [ \Magento\Backend\Console\Command\CacheDisableCommand::class, - $cacheDisable, + ['command' => 'cache:disable'], 'setup:cache:disable', [] ], diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobSetMaintenanceModeTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobSetMaintenanceModeTest.php index 450b9a44fcd81..141c59acd92cd 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobSetMaintenanceModeTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/Cron/JobSetMaintenanceModeTest.php @@ -1,6 +1,6 @@ 10 + ]; + + /** + * @var \Magento\Setup\Model\Address\AddressDataGenerator|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressGeneratorMock; + + /** + * @var \Magento\Setup\Model\Customer\CustomerDataGenerator + */ + private $customerGenerator; + + public function setUp() + { + $this->groupCollectionFactoryMock = $this->getMock( + \Magento\Customer\Model\ResourceModel\Group\CollectionFactory::class, + ['create', 'getAllIds'], + [], + '', + false + ); + + $this->groupCollectionFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->groupCollectionFactoryMock); + + $this->groupCollectionFactoryMock + ->expects($this->once()) + ->method('getAllIds') + ->willReturn([1]); + + $this->addressGeneratorMock = $this->getMock( + \Magento\Setup\Model\Address\AddressDataGenerator::class, + [], + [], + '', + false + ); + + $this->customerGenerator = new \Magento\Setup\Model\Customer\CustomerDataGenerator( + $this->groupCollectionFactoryMock, + $this->addressGeneratorMock, + $this->config + ); + } + + public function testEmail() + { + $customer = $this->customerGenerator->generate(42); + + $this->assertEquals('user_42@example.com', $customer['customer']['email']); + } + + public function testAddressGeneration() + { + $this->addressGeneratorMock + ->expects($this->exactly(10)) + ->method('generateAddress'); + + $customer = $this->customerGenerator->generate(42); + + $this->assertCount($this->config['addresses-count'], $customer['addresses']); + } + + public function testCustomerGroup() + { + $customer = $this->customerGenerator->generate(1); + $this->assertEquals(1, $customer['customer']['group_id']); + } + + public function testCustomerStructure() + { + $customer = $this->customerGenerator->generate(42); + + foreach ($this->customerStructure as $customerField) { + $this->assertTrue(array_key_exists($customerField, $customer)); + } + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/DataGeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/DataGeneratorTest.php new file mode 100644 index 0000000000000..4da979497ea54 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/DataGeneratorTest.php @@ -0,0 +1,50 @@ +generate($wordCount, $wordCount); + + $found = false; + foreach ($data as $word) { + $found = (strpos($result, $word[0]) !== false) || $found; + } + $this->assertTrue($found); + $this->assertEquals($wordCount, count(explode(" ", $result))); + } + + public function testGenerateWithKey() + { + $key = 'generate-test'; + + $data = file(__DIR__ . self::PATH_TO_CSV_FILE); + $wordCount = mt_rand(1, count($data)); + $model = new DataGenerator(__DIR__ . self::PATH_TO_CSV_FILE); + $result = $model->generate($wordCount, $wordCount, $key); + + $foundResult = $model->generate($wordCount, $wordCount, $key); + + $this->assertEquals($wordCount, count(explode(" ", $result))); + $this->assertEquals($result, $foundResult); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/DateTime/DateTimeProviderTest.php b/setup/src/Magento/Setup/Test/Unit/Model/DateTime/DateTimeProviderTest.php index 05992d69495f3..ffde07483b848 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/DateTime/DateTimeProviderTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/DateTime/DateTimeProviderTest.php @@ -1,6 +1,6 @@ [ + 'count-min' => 3, + 'count-max' => 3 + ], + 'mixin' => [ + 'tags' => ['p', 'b', 'div'] + ] + ]; + + /** + * @var array + */ + private $descriptionConfigWithoutMixin = [ + 'paragraphs' => [ + 'count-min' => 3, + 'count-max' => 3 + ] + ]; + + public function setUp() + { + $this->descriptionParagraphGeneratorMock = $this->getMock( + \Magento\Setup\Model\Description\DescriptionParagraphGenerator::class, + [], + [], + '', + false + ); + $this->descriptionParagraphGeneratorMock + ->expects($this->exactly(3)) + ->method('generate') + ->will($this->onConsecutiveCalls( + $this->paragraphs[0], + $this->paragraphs[1], + $this->paragraphs[2] + )); + + $this->mixinManagerMock = $this->getMock( + \Magento\Setup\Model\Description\MixinManager::class, + [], + [], + '', + false + ); + } + + public function testGeneratorWithMixin() + { + $descriptionWithMixin = 'Some description with mixin'; + $this->mixinManagerMock + ->expects($this->once()) + ->method('apply') + ->with( + implode(PHP_EOL, $this->paragraphs), + $this->descriptionConfigWithMixin['mixin']['tags'] + ) + ->willReturn($descriptionWithMixin); + + $generator = new \Magento\Setup\Model\Description\DescriptionGenerator( + $this->descriptionParagraphGeneratorMock, + $this->mixinManagerMock, + $this->descriptionConfigWithMixin + ); + + $this->assertEquals($descriptionWithMixin, $generator->generate()); + } + + public function testGeneratorWithoutMixin() + { + $generator = new \Magento\Setup\Model\Description\DescriptionGenerator( + $this->descriptionParagraphGeneratorMock, + $this->mixinManagerMock, + $this->descriptionConfigWithoutMixin + ); + + $this->assertEquals(implode(PHP_EOL, $this->paragraphs), $generator->generate()); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/DescriptionParagraphGeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/DescriptionParagraphGeneratorTest.php new file mode 100644 index 0000000000000..7ece974831ed8 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/DescriptionParagraphGeneratorTest.php @@ -0,0 +1,74 @@ + [ + 'count-min' => 4, + 'count-max' => 4 + ] + ]; + + public function setUp() + { + $this->sentenceGeneratorMock = $this->getMock( + \Magento\Setup\Model\Description\DescriptionSentenceGenerator::class, + [], + [], + '', + false + ); + $this->paragraphGenerator = new \Magento\Setup\Model\Description\DescriptionParagraphGenerator( + $this->sentenceGeneratorMock, + $this->paragraphConfig + ); + } + + /** + * + */ + public function testParagraphGeneration() + { + // @codingStandardsIgnoreStart + $consecutiveSentences = [ + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.', + 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.', + 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' + ]; + // @codingStandardsIgnoreEnd + + $this->sentenceGeneratorMock + ->expects($this->exactly(4)) + ->method('generate') + ->will($this->onConsecutiveCalls( + $consecutiveSentences[0], + $consecutiveSentences[1], + $consecutiveSentences[2], + $consecutiveSentences[3] + )); + + $this->assertEquals( + implode(' ', $consecutiveSentences), + $this->paragraphGenerator->generate() + ); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/DescriptionSentenceGeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/DescriptionSentenceGeneratorTest.php new file mode 100644 index 0000000000000..7246bb60523a2 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/DescriptionSentenceGeneratorTest.php @@ -0,0 +1,59 @@ + [ + 'count-min' => 7, + 'count-max' => 7 + ] + ]; + + public function setUp() + { + $this->dictionaryMock = $this->getMock(\Magento\Setup\Model\Dictionary::class, [], [], '', false); + $this->sentenceGenerator = new \Magento\Setup\Model\Description\DescriptionSentenceGenerator( + $this->dictionaryMock, + $this->sentenceConfig + ); + } + + public function testSentenceGeneration() + { + $this->dictionaryMock + ->expects($this->exactly(7)) + ->method('getRandWord') + ->will($this->onConsecutiveCalls( + 'Lorem', + 'ipsum', + 'dolor', + 'sit', + 'amet', + 'consectetur', + 'adipiscing' + )); + + $this->assertEquals( + 'Lorem ipsum dolor sit amet consectetur adipiscing.', + $this->sentenceGenerator->generate() + ); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/BoldMixinTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/BoldMixinTest.php new file mode 100644 index 0000000000000..3d00ad2d300da --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/BoldMixinTest.php @@ -0,0 +1,73 @@ +randomWordSelectorMock = $this->getMock( + \Magento\Setup\Model\Description\Mixin\Helper\RandomWordSelector::class, + [], + [], + '', + false + ); + $this->wordWrapperMock = $this->getMock( + \Magento\Setup\Model\Description\Mixin\Helper\WordWrapper::class, + [], + [], + '', + false + ); + + $this->mixin = new \Magento\Setup\Model\Description\Mixin\BoldMixin( + $this->randomWordSelectorMock, + $this->wordWrapperMock + ); + } + + public function testEmptyApply() + { + $this->assertEquals('', $this->mixin->apply('')); + } + + public function testApply() + { + $fixtureString = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; + $fixtureStringResult = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; + $randWordsFixture = ['Lorem', 'dolor']; + + $this->randomWordSelectorMock + ->expects($this->once()) + ->method('getRandomWords') + ->with($this->equalTo($fixtureString), $this->greaterThan(0)) + ->willReturn($randWordsFixture); + + $this->wordWrapperMock + ->expects($this->once()) + ->method('wrapWords') + ->with($fixtureString, $randWordsFixture, '%s') + ->willReturn($fixtureStringResult); + + $this->assertEquals($fixtureStringResult, $this->mixin->apply($fixtureString)); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/BrakeMixinTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/BrakeMixinTest.php new file mode 100644 index 0000000000000..873ac70cfbb94 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/BrakeMixinTest.php @@ -0,0 +1,45 @@ +mixin = new \Magento\Setup\Model\Description\Mixin\BrakeMixin(); + } + + /** + * @dataProvider getTestData + */ + public function testApply($subject, $expectedResult) + { + $this->assertEquals($expectedResult, $this->mixin->apply($subject)); + } + + public function getTestData() + { + return [ + ['', ''], + [ + 'Lorem ipsum dolor sit amet.' . PHP_EOL + . 'Consectetur adipiscing elit.' . PHP_EOL + . 'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + + 'Lorem ipsum dolor sit amet.' . PHP_EOL + . '
    ' . PHP_EOL + . 'Consectetur adipiscing elit.' . PHP_EOL + . '
    ' . PHP_EOL + . 'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' + ] + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/HeaderMixinTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/HeaderMixinTest.php new file mode 100644 index 0000000000000..d017238c1ac81 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/HeaderMixinTest.php @@ -0,0 +1,46 @@ +mixin = new \Magento\Setup\Model\Description\Mixin\HeaderMixin(); + } + + /** + * @dataProvider getTestData + */ + public function testApply($subject, $expectedResult) + { + $this->assertEquals($expectedResult, $this->mixin->apply($subject)); + } + + public function getTestData() + { + return [ + ['', ''], + [ + 'Lorem ipsum dolor sit amet.' . PHP_EOL + . 'Consectetur adipiscing elit.' . PHP_EOL + . 'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + + '

    Lorem ipsum

    ' . PHP_EOL + . 'Lorem ipsum dolor sit amet.' . PHP_EOL + . '

    Consectetur

    ' . PHP_EOL + . 'Consectetur adipiscing elit.' . PHP_EOL + . '

    Sed do eiusmod

    ' . PHP_EOL + . 'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' + ] + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/Helper/RandomWordSelectorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/Helper/RandomWordSelectorTest.php new file mode 100644 index 0000000000000..17033c410bba3 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/Helper/RandomWordSelectorTest.php @@ -0,0 +1,60 @@ +helper = new \Magento\Setup\Model\Description\Mixin\Helper\RandomWordSelector(); + } + + /** + * @param string $fixtureSource + * @param int $fixtureCount + * @dataProvider getTestData + */ + public function testRandomSelector($fixtureSource, $fixtureCount) + { + $randWords = $this->helper->getRandomWords($fixtureSource, $fixtureCount); + + $this->assertCount($fixtureCount, $randWords); + + $fixtureWords = str_word_count($fixtureSource, 1); + foreach ($randWords as $randWord) { + $this->assertTrue(in_array($randWord, $fixtureWords)); + } + } + + public function getTestData() + { + return [ + [ + 'source' => ' + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + ', + 'count' => 1 + ], + [ + 'source' => 'Lorem.', + 'count' => 5 + ], + [ + 'source' => ' + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + ', + 'count' => 3 + ], + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/Helper/WordWrapperTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/Helper/WordWrapperTest.php new file mode 100644 index 0000000000000..a31b720bf10c1 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/Helper/WordWrapperTest.php @@ -0,0 +1,64 @@ +wrapper = new \Magento\Setup\Model\Description\Mixin\Helper\WordWrapper(); + } + + /** + * @param array $inputData + * @param string $expectedResult + * @dataProvider getTestData + */ + public function testWrapping($inputData, $expectedResult) + { + $this->assertEquals( + $expectedResult, + $this->wrapper->wrapWords($inputData['source'], $inputData['words'], $inputData['format']) + ); + } + + public function getTestData() + { + return [ + [ + [ + 'source' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'words' => [], + 'format' => '', + ], + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' + ], + + [ + [ + 'source' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'words' => ['Lorem'], + 'format' => '%s', + ], + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' + ], + + [ + [ + 'source' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'words' => ['Lorem', 'consectetur', 'elit'], + 'format' => '%s', + ], + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' + ], + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/ItalicMixinTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/ItalicMixinTest.php new file mode 100644 index 0000000000000..49e9b8ee5cca5 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/ItalicMixinTest.php @@ -0,0 +1,73 @@ +randomWordSelectorMock = $this->getMock( + \Magento\Setup\Model\Description\Mixin\Helper\RandomWordSelector::class, + [], + [], + '', + false + ); + $this->wordWrapperMock = $this->getMock( + \Magento\Setup\Model\Description\Mixin\Helper\WordWrapper::class, + [], + [], + '', + false + ); + + $this->mixin = new \Magento\Setup\Model\Description\Mixin\ItalicMixin( + $this->randomWordSelectorMock, + $this->wordWrapperMock + ); + } + + public function testEmptyApply() + { + $this->assertEquals('', $this->mixin->apply('')); + } + + public function testApply() + { + $fixtureString = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; + $fixtureStringResult = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; + $randWordsFixture = ['Lorem', 'dolor']; + + $this->randomWordSelectorMock + ->expects($this->once()) + ->method('getRandomWords') + ->with($this->equalTo($fixtureString), $this->greaterThan(0)) + ->willReturn($randWordsFixture); + + $this->wordWrapperMock + ->expects($this->once()) + ->method('wrapWords') + ->with($fixtureString, $randWordsFixture, '%s') + ->willReturn($fixtureStringResult); + + $this->assertEquals($fixtureStringResult, $this->mixin->apply($fixtureString)); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/ParagraphMixinTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/ParagraphMixinTest.php new file mode 100644 index 0000000000000..44cb2433bb62c --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/ParagraphMixinTest.php @@ -0,0 +1,43 @@ +mixin = new \Magento\Setup\Model\Description\Mixin\ParagraphMixin(); + } + + /** + * @dataProvider getTestData + */ + public function testApply($subject, $expectedResult) + { + $this->assertEquals($expectedResult, $this->mixin->apply($subject)); + } + + public function getTestData() + { + return [ + ['', '

    '], + [ + 'Lorem ipsum dolor sit amet.' . PHP_EOL + . 'Consectetur adipiscing elit.' . PHP_EOL + . 'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + + '

    Lorem ipsum dolor sit amet.

    ' . PHP_EOL + . '

    Consectetur adipiscing elit.

    ' . PHP_EOL + . '

    Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

    ' + ] + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/SpanMixinTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/SpanMixinTest.php new file mode 100644 index 0000000000000..0376b8554a0b9 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/Mixin/SpanMixinTest.php @@ -0,0 +1,73 @@ +randomWordSelectorMock = $this->getMock( + \Magento\Setup\Model\Description\Mixin\Helper\RandomWordSelector::class, + [], + [], + '', + false + ); + $this->wordWrapperMock = $this->getMock( + \Magento\Setup\Model\Description\Mixin\Helper\WordWrapper::class, + [], + [], + '', + false + ); + + $this->mixin = new \Magento\Setup\Model\Description\Mixin\SpanMixin( + $this->randomWordSelectorMock, + $this->wordWrapperMock + ); + } + + public function testEmptyApply() + { + $this->assertEquals('', $this->mixin->apply('')); + } + + public function testApply() + { + $fixtureString = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; + $fixtureStringResult = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; + $randWordsFixture = ['Lorem', 'dolor']; + + $this->randomWordSelectorMock + ->expects($this->once()) + ->method('getRandomWords') + ->with($this->equalTo($fixtureString), $this->greaterThan(0)) + ->willReturn($randWordsFixture); + + $this->wordWrapperMock + ->expects($this->once()) + ->method('wrapWords') + ->with($fixtureString, $randWordsFixture, '%s') + ->willReturn($fixtureStringResult); + + $this->assertEquals($fixtureStringResult, $this->mixin->apply($fixtureString)); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/Description/MixinManagerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/Description/MixinManagerTest.php new file mode 100644 index 0000000000000..e9a57559de783 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/Description/MixinManagerTest.php @@ -0,0 +1,82 @@ +mixinFactoryMock = $this->getMock( + \Magento\Setup\Model\Description\Mixin\MixinFactory::class, + [], + [], + '', + false + ); + $this->mixinManager = new \Magento\Setup\Model\Description\MixinManager($this->mixinFactoryMock); + } + + public function testApply() + { + $description = '>o<'; + $mixinList = ['x', 'y', 'z']; + + $xMixinMock = $this->getMockForAbstractClass( + \Magento\Setup\Model\Description\Mixin\DescriptionMixinInterface::class + ); + $xMixinMock->expects($this->once()) + ->method('apply') + ->with($description) + ->willReturn($description . 'x'); + + $yMixinMock = $this->getMockForAbstractClass( + \Magento\Setup\Model\Description\Mixin\DescriptionMixinInterface::class + ); + $yMixinMock->expects($this->once()) + ->method('apply') + ->with($description . 'x') + ->willReturn($description . 'xy'); + + $zMixinMock = $this->getMockForAbstractClass( + \Magento\Setup\Model\Description\Mixin\DescriptionMixinInterface::class + ); + $zMixinMock->expects($this->once()) + ->method('apply') + ->with($description . 'xy') + ->willReturn($description . 'xyz'); + + $this->mixinFactoryMock + ->expects($this->exactly(count($mixinList))) + ->method('create') + ->withConsecutive( + [$mixinList[0]], + [$mixinList[1]], + [$mixinList[2]] + ) + ->will( + $this->onConsecutiveCalls( + $xMixinMock, + $yMixinMock, + $zMixinMock + ) + ); + + $this->assertEquals( + $description . 'xyz', + $this->mixinManager->apply($description, $mixinList) + ); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/DictionaryTest.php b/setup/src/Magento/Setup/Test/Unit/Model/DictionaryTest.php new file mode 100644 index 0000000000000..158c8a9736aaf --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/DictionaryTest.php @@ -0,0 +1,59 @@ +getRandWord(); + } + + /** + * @expectedException \Magento\Setup\Exception + * @expectedExceptionMessageRegExp /Dictionary file .*empty-dictionary\.csv is empty/ + */ + public function testDictionaryFileIsEmptyException() + { + $filePath = __DIR__ . '/_files/empty-dictionary.csv'; + file_put_contents($filePath, ''); + + try { + $dictionary = new \Magento\Setup\Model\Dictionary($filePath); + $dictionary->getRandWord(); + } finally { + unlink($filePath); + } + } + + public function testGetRandWord() + { + $filePath = __DIR__ . '/_files/valid-dictionary.csv'; + file_put_contents($filePath, implode(PHP_EOL, $this->dictionary)); + + $dictionary = new \Magento\Setup\Model\Dictionary($filePath); + + $this->assertTrue(in_array($dictionary->getRandWord(), $this->dictionary)); + $this->assertTrue(in_array($dictionary->getRandWord(), $this->dictionary)); + $this->assertTrue(in_array($dictionary->getRandWord(), $this->dictionary)); + + unlink($filePath); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/FixtureGenerator/SqlCollectorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/FixtureGenerator/SqlCollectorTest.php new file mode 100644 index 0000000000000..617743a047ef3 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/FixtureGenerator/SqlCollectorTest.php @@ -0,0 +1,123 @@ +resourceConnection = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->unit = (new ObjectManager($this))->getObject( + SqlCollector::class, + ['resourceConnection' => $this->resourceConnection] + ); + } + + public function testGetEmptySql() + { + $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->setMethods(['getProfiler']) + ->getMockForAbstractClass(); + $profiler = $this->getMockBuilder(\Zend_Db_Profiler::class) + ->disableOriginalConstructor() + ->getMock(); + $connection->expects($this->once())->method('getProfiler')->willReturn($profiler); + $this->resourceConnection->expects($this->once())->method('getConnection')->willReturn($connection); + + $profiler->expects($this->once())->method('getQueryProfiles')->willReturn([]); + + $this->unit->disable(); + $this->assertEquals([], $this->unit->getSql()); + } + + public function testGetEmptySqlWhenSelectQueryProcessed() + { + $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->setMethods(['getProfiler']) + ->getMockForAbstractClass(); + $profiler = $this->getMockBuilder(\Zend_Db_Profiler::class) + ->disableOriginalConstructor() + ->getMock(); + $connection->expects($this->once())->method('getProfiler')->willReturn($profiler); + $this->resourceConnection->expects($this->once())->method('getConnection')->willReturn($connection); + + $query = $this->getMockBuilder(\Zend_Db_Profiler_Query::class)->disableOriginalConstructor()->getMock(); + $query->expects($this->once())->method('getQueryType')->willReturn(\Zend_Db_Profiler::SELECT); + $profiler->expects($this->once())->method('getQueryProfiles')->willReturn([$query]); + + $this->unit->disable(); + $this->assertEquals([], $this->unit->getSql()); + } + + public function testGetSql() + { + $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->setMethods(['getProfiler']) + ->getMockForAbstractClass(); + $profiler = $this->getMockBuilder(\Zend_Db_Profiler::class) + ->disableOriginalConstructor() + ->getMock(); + $connection->expects($this->once())->method('getProfiler')->willReturn($profiler); + $this->resourceConnection->expects($this->once())->method('getConnection')->willReturn($connection); + + $query = $this->getMockBuilder(\Zend_Db_Profiler_Query::class)->disableOriginalConstructor()->getMock(); + $query->expects($this->once())->method('getQueryType')->willReturn(\Zend_Db_Profiler::INSERT); + $query->expects($this->once())->method('getQuery')->willReturn( + 'INSERT INTO `catalog_product_entity` (id, sku, type, created_at, attribute_set)' + . ' VALUES (?, ?, ?, \'2013-12-11\', ?), (?, ?, ?, \'2013-12-11\', ?)' + ); + $query->expects($this->once())->method('getQueryParams')->willReturn([ + 4, 'sku_4', 'simple', 4, 5, 'sku_5', 'simple', 12 + ]); + $profiler->expects($this->once())->method('getQueryProfiles')->willReturn([$query]); + + $this->unit->disable(); + $this->assertEquals( + [ + [ + [ + [ + 'id' => 4, + 'sku' => 'sku_4', + 'type' => 'simple', + 'created_at' => '2013-12-11', + 'attribute_set' => 4, + ], + [ + 'id' => 5, + 'sku' => 'sku_5', + 'type' => 'simple', + 'created_at' => '2013-12-11', + 'attribute_set' => 12, + ], + ], + 'catalog_product_entity' + ] + ], + $this->unit->getSql() + ); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/GeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/GeneratorTest.php index 670fa5420699e..5e71e1d37f94a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/GeneratorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/GeneratorTest.php @@ -1,6 +1,6 @@ getComposerInformation(); - $timeZoneProvider = $this->getMock(\Magento\Setup\Model\DateTime\TimeZoneProvider::class, [], [], '', false); + $this->composerInformation = $this->getComposerInformation(); + $this->timeZoneProvider = $this->getMockBuilder(\Magento\Setup\Model\DateTime\TimeZoneProvider::class) + ->disableOriginalConstructor() + ->getMock(); $timeZone = $this->getMock(\Magento\Framework\Stdlib\DateTime\Timezone::class, [], [], '', false); - $timeZoneProvider->expects($this->any())->method('get')->willReturn($timeZone); - $packagesAuth = $this->getMock(\Magento\Setup\Model\PackagesAuth::class, [], [], '', false); - $filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false); - $objectManagerProvider = $this->getMock(\Magento\Setup\Model\ObjectManagerProvider::class, [], [], '', false); + $this->timeZoneProvider->expects($this->any())->method('get')->willReturn($timeZone); + $this->packagesAuth = $this->getMock(\Magento\Setup\Model\PackagesAuth::class, [], [], '', false); + $this->filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false); + $this->objectManagerProvider = $this->getMockBuilder(\Magento\Setup\Model\ObjectManagerProvider::class) + ->disableOriginalConstructor() + ->getMock(); $objectManager = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); - $applicationFactory = $this->getMock( - \Magento\Framework\Composer\MagentoComposerApplicationFactory::class, - [], - [], - '', - false - ); + $appFactory = $this->getMockBuilder(\Magento\Framework\Composer\MagentoComposerApplicationFactory::class) + ->disableOriginalConstructor() + ->getMock(); $application = $this->getMock(\Magento\Composer\MagentoComposerApplication::class, [], [], '', false); $application->expects($this->any()) ->method('runComposerCommand') - ->willReturn('versions: 2.0.1'); - $applicationFactory->expects($this->any())->method('create')->willReturn($application); + ->willReturnMap([ + [ + [ + PackagesData::PARAM_COMMAND => PackagesData::COMPOSER_SHOW, + PackagesData::PARAM_PACKAGE => 'magento/package-1', + PackagesData::PARAM_AVAILABLE => true, + ], + null, + 'versions: 2.0.1' + ], + [ + [ + PackagesData::PARAM_COMMAND => PackagesData::COMPOSER_SHOW, + PackagesData::PARAM_PACKAGE => 'magento/package-2', + PackagesData::PARAM_AVAILABLE => true, + ], + null, + 'versions: 2.0.1' + ], + [ + [ + PackagesData::PARAM_COMMAND => PackagesData::COMPOSER_SHOW, + PackagesData::PARAM_PACKAGE => 'partner/package-3', + PackagesData::PARAM_AVAILABLE => true, + ], + null, + 'versions: 3.0.1' + ], + ]); + $appFactory->expects($this->any())->method('create')->willReturn($application); $objectManager->expects($this->any()) ->method('get') ->with(\Magento\Framework\Composer\MagentoComposerApplicationFactory::class) - ->willReturn($applicationFactory); - $objectManagerProvider->expects($this->any())->method('get')->willReturn($objectManager); + ->willReturn($appFactory); + $this->objectManagerProvider->expects($this->any())->method('get')->willReturn($objectManager); $directoryWrite = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\WriteInterface::class); $directoryRead = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\ReadInterface::class); - $filesystem->expects($this->any())->method('getDirectoryRead')->will($this->returnValue($directoryRead)); - $filesystem->expects($this->any()) + $this->filesystem->expects($this->any())->method('getDirectoryRead')->will($this->returnValue($directoryRead)); + $this->filesystem->expects($this->any()) ->method('getDirectoryWrite') ->will($this->returnValue($directoryWrite)); $directoryWrite->expects($this->any())->method('isExist')->willReturn(true); @@ -81,32 +139,41 @@ public function setUp() . '}}}' ); - $typeMapper = $this->getMockBuilder(\Magento\Setup\Model\Grid\TypeMapper::class) + $this->typeMapper = $this->getMockBuilder(\Magento\Setup\Model\Grid\TypeMapper::class) ->disableOriginalConstructor() ->getMock(); - $typeMapper->expects(static::any()) + $this->typeMapper->expects(static::any()) ->method('map') ->willReturnMap([ [ComposerInformation::MODULE_PACKAGE_TYPE, \Magento\Setup\Model\Grid\TypeMapper::MODULE_PACKAGE_TYPE], ]); + $this->createPackagesData(); + } + + private function createPackagesData() + { $this->packagesData = new PackagesData( - $composerInformation, - $timeZoneProvider, - $packagesAuth, - $filesystem, - $objectManagerProvider, - $typeMapper + $this->composerInformation, + $this->timeZoneProvider, + $this->packagesAuth, + $this->filesystem, + $this->objectManagerProvider, + $this->typeMapper ); } /** + * @param array $requiredPackages + * @param array $installedPackages + * @param array $repo * @return ComposerInformation|MockObject */ - private function getComposerInformation() + private function getComposerInformation($requiredPackages = [], $installedPackages = [], $repo = []) { $composerInformation = $this->getMock(ComposerInformation::class, [], [], '', false); $composerInformation->expects($this->any())->method('getInstalledMagentoPackages')->willReturn( + $installedPackages ?: [ 'magento/package-1' => [ 'name' => 'magento/package-1', @@ -117,21 +184,30 @@ private function getComposerInformation() 'name' => 'magento/package-2', 'type' => 'magento2-module', 'version'=> '1.0.1' - ] + ], + 'partner/package-3' => [ + 'name' => 'partner/package-3', + 'type' => 'magento2-module', + 'version'=> '3.0.0' + ], ] ); $composerInformation->expects($this->any())->method('getRootRepositories') - ->willReturn(['repo1', 'repo2']); + ->willReturn($repo ?: ['repo1', 'repo2']); $composerInformation->expects($this->any())->method('getPackagesTypes') ->willReturn(['magento2-module']); $rootPackage = $this->getMock(RootPackage::class, [], ['magento/project', '2.1.0', '2']); $rootPackage->expects($this->any()) ->method('getRequires') - ->willReturn([ - 'magento/package-1' => '1.0.0', - 'magento/package-2' => '1.0.1' - ]); + ->willReturn( + $requiredPackages ?: + [ + 'magento/package-1' => '1.0.0', + 'magento/package-2' => '1.0.1', + 'partner/package-3' => '3.0.0', + ] + ); $composerInformation->expects($this->any()) ->method('getRootPackage') ->willReturn($rootPackage); @@ -146,19 +222,57 @@ public function testSyncPackagesData() $this->assertArrayHasKey('date', $latestData['lastSyncDate']); $this->assertArrayHasKey('time', $latestData['lastSyncDate']); $this->assertArrayHasKey('packages', $latestData); - $this->assertSame(2, count($latestData['packages'])); - $this->assertSame(2, $latestData['countOfUpdate']); + $this->assertSame(3, count($latestData['packages'])); + $this->assertSame(3, $latestData['countOfUpdate']); $this->assertArrayHasKey('installPackages', $latestData); $this->assertSame(1, count($latestData['installPackages'])); $this->assertSame(1, $latestData['countOfInstall']); } - public function testGetPackagesForUpdate() + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Couldn't get available versions for package partner/package-4 + */ + public function testGetPackagesForUpdateWithException() { + $requiredPackages = [ + 'partner/package-4' => '4.0.4', + ]; + $installedPackages = [ + 'partner/package-4' => [ + 'name' => 'partner/package-4', + 'type' => 'magento2-module', + 'version'=> '4.0.4' + ], + ]; + $this->composerInformation = $this->getComposerInformation($requiredPackages, $installedPackages); + $this->createPackagesData(); + $this->packagesData->getPackagesForUpdate(); + } + + public function testPackagesForUpdateFromJson() + { + $this->composerInformation = $this->getComposerInformation([], [], ['https://repo1']); + $this->packagesAuth->expects($this->atLeastOnce()) + ->method('getCredentialBaseUrl') + ->willReturn('repo1'); + $this->createPackagesData(); $packages = $this->packagesData->getPackagesForUpdate(); $this->assertEquals(2, count($packages)); $this->assertArrayHasKey('magento/package-1', $packages); + $this->assertArrayHasKey('partner/package-3', $packages); + $firstPackage = array_values($packages)[0]; + $this->assertArrayHasKey('latestVersion', $firstPackage); + $this->assertArrayHasKey('versions', $firstPackage); + } + + public function testGetPackagesForUpdate() + { + $packages = $this->packagesData->getPackagesForUpdate(); + $this->assertEquals(3, count($packages)); + $this->assertArrayHasKey('magento/package-1', $packages); $this->assertArrayHasKey('magento/package-2', $packages); + $this->assertArrayHasKey('partner/package-3', $packages); $firstPackage = array_values($packages)[0]; $this->assertArrayHasKey('latestVersion', $firstPackage); $this->assertArrayHasKey('versions', $firstPackage); @@ -167,9 +281,10 @@ public function testGetPackagesForUpdate() public function testGetInstalledPackages() { $installedPackages = $this->packagesData->getInstalledPackages(); - $this->assertEquals(2, count($installedPackages)); + $this->assertEquals(3, count($installedPackages)); $this->assertArrayHasKey('magento/package-1', $installedPackages); $this->assertArrayHasKey('magento/package-2', $installedPackages); + $this->assertArrayHasKey('partner/package-3', $installedPackages); } public function testGetMetaPackagesMap() diff --git a/setup/src/Magento/Setup/Test/Unit/Model/PayloadValidatorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/PayloadValidatorTest.php index 2b1d8529d7818..27f6ec8151e6b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/PayloadValidatorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/PayloadValidatorTest.php @@ -1,6 +1,6 @@ descriptionGeneratorMock = $this->getMock( + \Magento\Setup\Model\Description\DescriptionGenerator::class, + [], + [], + '', + false + ); + $this->searchTermManagerMock = $this->getMock( + \Magento\Setup\Model\SearchTermManager::class, + [], + [], + '', + false + ); + + $this->searchTermDescriptionGenerator = new \Magento\Setup\Model\SearchTermDescriptionGenerator( + $this->descriptionGeneratorMock, + $this->searchTermManagerMock + ); + } + + public function testGeneratorWithCaching() + { + $descriptionMock = ''; + $firstProductIndex = 1; + $secondProductIndex = 2; + + $this->descriptionGeneratorMock + ->expects($this->once()) + ->method('generate') + ->willReturn($descriptionMock); + + $this->searchTermManagerMock + ->expects($this->exactly(2)) + ->method('applySearchTermsToDescription') + ->withConsecutive( + [$descriptionMock, $firstProductIndex], + [$descriptionMock, $secondProductIndex] + ); + + $this->searchTermDescriptionGenerator->generate($firstProductIndex); + $this->searchTermDescriptionGenerator->generate($secondProductIndex); + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/SearchTermManagerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/SearchTermManagerTest.php new file mode 100644 index 0000000000000..0ca70d36345c1 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/SearchTermManagerTest.php @@ -0,0 +1,76 @@ + 'x-wing', + 'count' => '33' + ], [ + 'term' => 'tie-fighter', + 'count' => '100' + ], [ + 'term' => 'n-1 starfighter', + 'count' => '42' + ], + ]; + + /** + * @var array + */ + private $searchTermsUsage = [ + 'x-wing' => [ + 'used' => 0 + ], + 'tie-fighter' => [ + 'used' => 0 + ], + 'n-1 starfighter' => [ + 'used' => 0 + ] + ]; + + public function setUp() + { + $this->searchTermManager = new \Magento\Setup\Model\SearchTermManager( + $this->searchTermConfiguration, + $this->totalProductsCount + ); + } + + public function testSearchTermApplied() + { + for ($productIndex=1; $productIndex<=$this->totalProductsCount; $productIndex++) { + $description = 'Fleet: '; + $this->searchTermManager->applySearchTermsToDescription($description, $productIndex); + + foreach (array_keys($this->searchTermsUsage) as $searchTerm) { + if (preg_match("/\\b$searchTerm\\b/", $description)) { + $this->searchTermsUsage[$searchTerm]['used']++; + } + } + } + + foreach ($this->searchTermConfiguration as $searchTerm) { + $this->assertEquals($searchTerm['count'], $this->searchTermsUsage[$searchTerm['term']]['used']); + } + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/StoreConfigurationDataMapperTest.php b/setup/src/Magento/Setup/Test/Unit/Model/StoreConfigurationDataMapperTest.php index 67792c93ee71d..902763d8821b9 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/StoreConfigurationDataMapperTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/StoreConfigurationDataMapperTest.php @@ -1,6 +1,6 @@ assertEquals([], $returnValue->getData()); } - public function testCreateDefinitionsConfig() - { - $testData = [ConfigOptionsListConstants::INPUT_KEY_DEFINITION_FORMAT => 'test-format']; - $returnValue = $this->configGeneratorObject->createDefinitionsConfig($testData); - $this->assertEquals(ConfigFilePool::APP_ENV, $returnValue->getFileKey()); - $this->assertEquals(['definition' => ['format' => 'test-format']], $returnValue->getData()); - } - public function testCreateDbConfig() { $testData = [ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php index 8b887ab7ba002..4c21ce39cdfb6 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/ConnectionFactoryTest.php @@ -1,6 +1,6 @@ logMock = $this->getMockBuilder(\Magento\Setup\Module\Di\Compiler\Log\Log::class) @@ -66,7 +64,7 @@ protected function setUp() $this->classReaderMock, $this->classesScanner, $this->validatorMock, - '/var/generation' + '/generated/code' ); } @@ -111,7 +109,7 @@ public function testGetList() public function testGetListNoValidation() { - $path = '/var/generation'; + $path = '/generated/code'; $classes = ['NameSpace1\ClassName1', 'NameSpace1\ClassName2']; diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/InstancesNamesList/InterceptionsTest.php b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/InstancesNamesList/InterceptionsTest.php index 212575e644d7c..16e1595f5d076 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/InstancesNamesList/InterceptionsTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Reader/InstancesNamesList/InterceptionsTest.php @@ -1,6 +1,6 @@ logMock = $this->getMockBuilder(\Magento\Setup\Module\Di\Compiler\Log\Log::class) @@ -97,7 +100,7 @@ public function testGetList() public function testGetListNoValidation() { - $path = '/var/generation'; + $path = '/generated/code'; $classes = ['NameSpace1\ClassName1', 'NameSpace1\ClassName2']; diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Scanner/ArrayScannerTest.php b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Scanner/ArrayScannerTest.php index 453396c3981ec..c29c5dd3a50df 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Scanner/ArrayScannerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/Code/Scanner/ArrayScannerTest.php @@ -1,6 +1,6 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/etc/di.xml b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/etc/di.xml index 47deb25b5070d..0762ca67ec894 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/etc/di.xml +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/etc/di.xml @@ -1,7 +1,7 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/etc/source/PhpExt.php/content b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/etc/source/PhpExt.php/content index 3c2daa452ee8d..af373f0f2eddd 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/etc/source/PhpExt.php/content +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/etc/source/PhpExt.php/content @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/view/frontend/default.xml b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/view/frontend/default.xml index 119ace1d0e356..2dd9e4557d316 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/view/frontend/default.xml +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/code/Magento/SomeModule/view/frontend/default.xml @@ -1,7 +1,7 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/design/adminhtml/default/backend/layout.xml b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/design/adminhtml/default/backend/layout.xml index a8096b9620b85..0a9bcfa548481 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/design/adminhtml/default/backend/layout.xml +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/design/adminhtml/default/backend/layout.xml @@ -1,7 +1,7 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/additional.xml b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/additional.xml index a8096b9620b85..0a9bcfa548481 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/additional.xml +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/additional.xml @@ -1,7 +1,7 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/config.xml b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/config.xml index 20a064f70bc1b..828f16428a58d 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/config.xml +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/config.xml @@ -1,7 +1,7 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/di/config.xml b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/di/config.xml index 17af1192e5a70..77251c257c8ee 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/di/config.xml +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/app/etc/di/config.xml @@ -1,7 +1,7 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/extension_attributes.xml b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/extension_attributes.xml index e79052491cbc4..14e5c0662ef44 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/extension_attributes.xml +++ b/setup/src/Magento/Setup/Test/Unit/Module/Di/_files/extension_attributes.xml @@ -1,7 +1,7 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php index 9bcac1e73ccee..331d464e45785 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php @@ -1,6 +1,6 @@ _handler = STDOUT; - } - public function testThatHandlerIsRight() { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - /** @var \Magento\Setup\Module\I18n\Dictionary\Writer\Csv $writer */ - $writer = $objectManagerHelper->getObject(\Magento\Setup\Module\I18n\Dictionary\Writer\Csv\Stdo::class); - - $this->assertAttributeEquals($this->_handler, '_fileHandler', $writer); + $handler = STDOUT; + // Mocking object's under test destructor here is perfectly valid as there is no way to reopen STDOUT + $writer = $this->getMock(Stdo::class, ['__destruct']); + $this->assertAttributeEquals($handler, '_fileHandler', $writer); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/Writer/CsvTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/Writer/CsvTest.php index 53c0355403c67..28b9aa455413f 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/Writer/CsvTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Dictionary/Writer/CsvTest.php @@ -1,6 +1,6 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/Adapter/_files/email.html b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/Adapter/_files/email.html index c25db093ecd33..aa894528348f1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/Adapter/_files/email.html +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/Adapter/_files/email.html @@ -1,6 +1,6 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/Adapter/_files/file.js b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/Adapter/_files/file.js index 4b7a3cb8df84a..d835e64d630e8 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/Adapter/_files/file.js +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/Adapter/_files/file.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/ParserTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/ParserTest.php index 162264c39514a..bb031cc7f1339 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/ParserTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/ParserTest.php @@ -1,6 +1,6 @@ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/_files/files_collector/file.js b/setup/src/Magento/Setup/Test/Unit/Module/I18n/_files/files_collector/file.js index 51ed125d3b1f3..094a9766127a5 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/_files/files_collector/file.js +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/_files/files_collector/file.js @@ -1,5 +1,5 @@ /** - * Copyright © 2016 Magento. All rights reserved. + * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ diff --git a/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php b/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php index baf7eb3d01a1a..1bd2b7fda4ca3 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/ResourceFactoryTest.php @@ -1,6 +1,6 @@ withConsecutive( [ \Magento\Framework\App\Filesystem\DirectoryList::class, - $this->isInstanceOf(\Magento\Framework\App\Filesystem\DirectoryList::class) + $this->isInstanceOf(\Magento\Framework\App\Filesystem\DirectoryList::class), ], [ \Magento\Framework\Filesystem::class, - $this->isInstanceOf(\Magento\Framework\Filesystem::class) + $this->isInstanceOf(\Magento\Framework\Filesystem::class), ] ); $mvcApplication->expects($this->any())->method('getServiceManager')->willReturn($serviceManager); @@ -130,10 +129,10 @@ public function testCreateService($zfAppConfig, $env, $cliParam, $expectedArray) $request->expects($this->any()) ->method('getContent') ->willReturn( - $cliParam ? ['install', '--magento-init-params=' . $cliParam ] : ['install'] + $cliParam ? ['install', '--magento-init-params=' . $cliParam] : ['install'] ); $mvcApplication->expects($this->any())->method('getConfig')->willReturn( - $zfAppConfig ? [InitParamListener::BOOTSTRAP_PARAM => $zfAppConfig]:[] + $zfAppConfig ? [InitParamListener::BOOTSTRAP_PARAM => $zfAppConfig] : [] ); $mvcApplication->expects($this->any())->method('getRequest')->willReturn($request); @@ -150,41 +149,55 @@ public function createServiceDataProvider() 'mage_mode App' => [['MAGE_MODE' => 'developer'], [], '', ['MAGE_MODE' => 'developer']], 'mage_mode Env' => [[], ['MAGE_MODE' => 'developer'], '', ['MAGE_MODE' => 'developer']], 'mage_mode CLI' => [[], [], 'MAGE_MODE=developer', ['MAGE_MODE' => 'developer']], - 'one MAGE_DIRS CLI' => [[], [], 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer']], + 'one MAGE_DIRS CLI' => [ + [], + [], + 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2', + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer'], + ], 'two MAGE_DIRS CLI' => [ [], [], 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2&MAGE_DIRS[cache][path]=/tmp/cache', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], - 'MAGE_MODE' => 'developer']], + [ + 'MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], + 'MAGE_MODE' => 'developer', + ], + ], 'mage_mode only' => [[], [], 'MAGE_MODE=developer', ['MAGE_MODE' => 'developer']], 'MAGE_DIRS Env' => [ [], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer'], '', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer'], + ], 'two MAGE_DIRS' => [ [], [], 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2&MAGE_DIRS[cache][path]=/tmp/cache', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], - 'MAGE_MODE' => 'developer']], + [ + 'MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], + 'MAGE_MODE' => 'developer', + ], + ], 'Env overwrites App' => [ ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/App']], 'MAGE_MODE' => 'developer'], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']], 'MAGE_MODE' => 'developer'], '', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']], 'MAGE_MODE' => 'developer']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']], 'MAGE_MODE' => 'developer'], + ], 'CLI overwrites Env' => [ ['MAGE_MODE' => 'developerApp'], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']]], 'MAGE_DIRS[base][path]=/var/www/magento2/CLI', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'developerApp']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'developerApp'], + ], 'CLI overwrites All' => [ ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/App']], 'MAGE_MODE' => 'production'], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']]], 'MAGE_DIRS[base][path]=/var/www/magento2/CLI', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'production']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'production'], + ], ]; } @@ -226,6 +239,168 @@ private function prepareEventManager() [$this->listener, 'onBootstrap'] )->willReturn($this->callbackHandler); $eventManager->expects($this->once())->method('getSharedManager')->willReturn($sharedManager); + return $eventManager; } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testAuthPreDispatch() + { + $eventMock = $this->getMockBuilder(\Zend\Mvc\MvcEvent::class) + ->disableOriginalConstructor() + ->getMock(); + $routeMatchMock = $this->getMockBuilder(\Zend\Mvc\Router\Http\RouteMatch::class) + ->disableOriginalConstructor() + ->getMock(); + $applicationMock = $this->getMockBuilder(\Zend\Mvc\Application::class) + ->disableOriginalConstructor() + ->getMock(); + $serviceManagerMock = $this->getMockBuilder(\Zend\ServiceManager\ServiceManager::class) + ->disableOriginalConstructor() + ->getMock(); + $deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + ->disableOriginalConstructor() + ->getMock(); + $deploymentConfigMock->expects($this->once()) + ->method('isAvailable') + ->willReturn(true); + $omProvider = $this->getMockBuilder(\Magento\Setup\Model\ObjectManagerProvider::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManagerMock = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); + $adminAppStateMock = $this->getMockBuilder(\Magento\Framework\App\State::class) + ->disableOriginalConstructor() + ->getMock(); + $sessionConfigMock = $this->getMockBuilder(\Magento\Backend\Model\Session\AdminConfig::class) + ->disableOriginalConstructor() + ->getMock(); + $backendAppListMock = $this->getMockBuilder(\Magento\Backend\App\BackendAppList::class) + ->disableOriginalConstructor() + ->getMock(); + $backendAppMock = $this->getMockBuilder(\Magento\Backend\App\BackendApp::class) + ->disableOriginalConstructor() + ->getMock(); + $backendUrlFactoryMock = $this->getMockBuilder(\Magento\Backend\Model\UrlFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $backendUrlMock = $this->getMockBuilder(\Magento\Backend\Model\Url::class) + ->disableOriginalConstructor() + ->getMock(); + $authenticationMock = $this->getMockBuilder(\Magento\Backend\Model\Auth::class) + ->disableOriginalConstructor() + ->getMock(); + $adminSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class) + ->disableOriginalConstructor() + ->getMock(); + $responseMock = $this->getMockBuilder(\Zend\Http\Response::class) + ->disableOriginalConstructor() + ->getMock(); + $headersMock = $this->getMockBuilder(\Zend\Http\Headers::class) + ->disableOriginalConstructor() + ->getMock(); + + $routeMatchMock->expects($this->once()) + ->method('getParam') + ->with('controller') + ->willReturn('testController'); + $eventMock->expects($this->once()) + ->method('getRouteMatch') + ->willReturn($routeMatchMock); + $eventMock->expects($this->once()) + ->method('getApplication') + ->willReturn($applicationMock); + $serviceManagerMock->expects($this->any()) + ->method('get') + ->willReturnMap( + [ + [ + \Magento\Framework\App\DeploymentConfig::class, + true, + $deploymentConfigMock, + ], + [ + \Magento\Setup\Model\ObjectManagerProvider::class, + true, + $omProvider, + ], + ] + ); + $objectManagerMock->expects($this->any()) + ->method('get') + ->willReturnMap( + [ + [ + \Magento\Framework\App\State::class, + $adminAppStateMock, + ], + [ + \Magento\Backend\Model\Session\AdminConfig::class, + $sessionConfigMock, + ], + [ + \Magento\Backend\App\BackendAppList::class, + $backendAppListMock, + ], + [ + \Magento\Backend\Model\UrlFactory::class, + $backendUrlFactoryMock, + ], + [ + \Magento\Backend\Model\Auth::class, + $authenticationMock, + ], + ] + ); + $objectManagerMock->expects($this->any()) + ->method('create') + ->willReturn($adminSessionMock); + $omProvider->expects($this->once()) + ->method('get') + ->willReturn($objectManagerMock); + $adminAppStateMock->expects($this->once()) + ->method('setAreaCode') + ->with(\Magento\Framework\App\Area::AREA_ADMINHTML); + $applicationMock->expects($this->once()) + ->method('getServiceManager') + ->willReturn($serviceManagerMock); + $backendAppMock->expects($this->once()) + ->method('getCookiePath') + ->willReturn(''); + $backendUrlFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($backendUrlMock); + $backendAppListMock->expects($this->once()) + ->method('getBackendApp') + ->willReturn($backendAppMock); + $authenticationMock->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $adminSessionMock->expects($this->once()) + ->method('isAllowed') + ->with('Magento_Backend::setup_wizard', null) + ->willReturn(false); + $adminSessionMock->expects($this->once()) + ->method('destroy'); + $eventMock->expects($this->once()) + ->method('getResponse') + ->willReturn($responseMock); + $responseMock->expects($this->once()) + ->method('getHeaders') + ->willReturn($headersMock); + $headersMock->expects($this->once()) + ->method('addHeaderLine'); + $responseMock->expects($this->once()) + ->method('setStatusCode') + ->with(302); + $eventMock->expects($this->once()) + ->method('stopPropagation'); + + $this->assertSame( + $this->listener->authPreDispatch($eventMock), + $responseMock + ); + } } diff --git a/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php b/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php index c263531979573..b04b173e652b9 100644 --- a/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/add-database.phtml b/setup/view/magento/setup/add-database.phtml index 6d25db2e08444..3771a66447381 100644 --- a/setup/view/magento/setup/add-database.phtml +++ b/setup/view/magento/setup/add-database.phtml @@ -1,6 +1,6 @@
    -
    \ No newline at end of file +
    diff --git a/setup/view/magento/setup/customize-your-store.phtml b/setup/view/magento/setup/customize-your-store.phtml index e16362721dfde..9cfab20171a0e 100644 --- a/setup/view/magento/setup/customize-your-store.phtml +++ b/setup/view/magento/setup/customize-your-store.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/extension-grid.phtml b/setup/view/magento/setup/extension-grid.phtml index 1096adca72043..45508cfe186ff 100644 --- a/setup/view/magento/setup/extension-grid.phtml +++ b/setup/view/magento/setup/extension-grid.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/home.phtml b/setup/view/magento/setup/home.phtml index f5783f07e0a4a..bd0151f23d69c 100644 --- a/setup/view/magento/setup/home.phtml +++ b/setup/view/magento/setup/home.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/index.phtml b/setup/view/magento/setup/index.phtml index ba9096c5766c6..81e39cb64fd0a 100644 --- a/setup/view/magento/setup/index.phtml +++ b/setup/view/magento/setup/index.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/install-extension-grid.phtml b/setup/view/magento/setup/install-extension-grid.phtml index e2bcbf9a777c8..3cb59b01f9d9c 100644 --- a/setup/view/magento/setup/install-extension-grid.phtml +++ b/setup/view/magento/setup/install-extension-grid.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/install.phtml b/setup/view/magento/setup/install.phtml index 06eb1a082ca66..01785701b0ed5 100644 --- a/setup/view/magento/setup/install.phtml +++ b/setup/view/magento/setup/install.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/marketplace-credentials.phtml b/setup/view/magento/setup/marketplace-credentials.phtml index f5357c781d191..8b7a11c030ec7 100644 --- a/setup/view/magento/setup/marketplace-credentials.phtml +++ b/setup/view/magento/setup/marketplace-credentials.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/module-grid.phtml b/setup/view/magento/setup/module-grid.phtml index 3f4f755ae3f6d..5576f823f1d10 100644 --- a/setup/view/magento/setup/module-grid.phtml +++ b/setup/view/magento/setup/module-grid.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/navigation/header-bar.phtml b/setup/view/magento/setup/navigation/header-bar.phtml index 540bfcfed6359..698ac25346f43 100644 --- a/setup/view/magento/setup/navigation/header-bar.phtml +++ b/setup/view/magento/setup/navigation/header-bar.phtml @@ -1,6 +1,6 @@
    - \ No newline at end of file + diff --git a/setup/view/magento/setup/navigation/menu.phtml b/setup/view/magento/setup/navigation/menu.phtml index ae94a891255dc..b7e5052e4231d 100644 --- a/setup/view/magento/setup/navigation/menu.phtml +++ b/setup/view/magento/setup/navigation/menu.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/readiness-check.phtml b/setup/view/magento/setup/readiness-check.phtml index 2aaf4de649bf0..018e3274f0167 100644 --- a/setup/view/magento/setup/readiness-check.phtml +++ b/setup/view/magento/setup/readiness-check.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/readiness-check/progress.phtml b/setup/view/magento/setup/readiness-check/progress.phtml index 6719d825dfcde..c2d21b911cbee 100755 --- a/setup/view/magento/setup/readiness-check/progress.phtml +++ b/setup/view/magento/setup/readiness-check/progress.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/start-updater.phtml b/setup/view/magento/setup/start-updater.phtml index fd31f994c3923..c4d226ceaab0b 100644 --- a/setup/view/magento/setup/start-updater.phtml +++ b/setup/view/magento/setup/start-updater.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/success.phtml b/setup/view/magento/setup/success.phtml index c220d91e7d058..7931bc670177e 100644 --- a/setup/view/magento/setup/success.phtml +++ b/setup/view/magento/setup/success.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/update-extension-grid.phtml b/setup/view/magento/setup/update-extension-grid.phtml index 62c9b1217f2a5..e26df4d90a2c8 100644 --- a/setup/view/magento/setup/update-extension-grid.phtml +++ b/setup/view/magento/setup/update-extension-grid.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/updater-success.phtml b/setup/view/magento/setup/updater-success.phtml index 07855edcaafc7..da91d44ef1efd 100644 --- a/setup/view/magento/setup/updater-success.phtml +++ b/setup/view/magento/setup/updater-success.phtml @@ -1,6 +1,6 @@ diff --git a/setup/view/magento/setup/web-configuration.phtml b/setup/view/magento/setup/web-configuration.phtml index 35aa19484ed53..2fbc5f20d21d6 100644 --- a/setup/view/magento/setup/web-configuration.phtml +++ b/setup/view/magento/setup/web-configuration.phtml @@ -1,6 +1,6 @@
    - +
    @@ -40,6 +40,13 @@ $hints = [

    {{$state.current.header}}

    +
    + {{validateUrl.failed}} +
    +